diff --git a/BUILD.gn b/BUILD.gn
index 533f7c1..d24c2da 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -682,6 +682,7 @@
       "//testing/libfuzzer/fuzzers",
       "//testing/libfuzzer/tests:libfuzzer_tests",
       "//third_party/icu/fuzzers",
+      "//third_party/qcms:fuzzers",
     ]
 
     # TODO(miu): Remove this dependency once the build configuration in
diff --git a/DEPS b/DEPS
index 32714fc4..f4a9f797 100644
--- a/DEPS
+++ b/DEPS
@@ -44,7 +44,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling V8
   # and whatever else without interference from each other.
-  'v8_revision': '2e896f6f6039add01a0d1d5efe6d5efe942c38a1',
+  'v8_revision': 'fe9bb7e6e251159852770160cfb21dad3cf03523',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling swarming_client
   # and whatever else without interference from each other.
@@ -96,7 +96,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling catapult
   # and whatever else without interference from each other.
-  'catapult_revision': '41bda73617edc29dbca3eda9b97a7b33f665c393',
+  'catapult_revision': '4709b3a00e515aaac4e58b904dfbf1622c7a340a',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling libFuzzer
   # and whatever else without interference from each other.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPageView.java b/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPageView.java
index ead68bc..5a5d768 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPageView.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPageView.java
@@ -115,6 +115,7 @@
 
     private float mUrlFocusChangePercent;
     private boolean mDisableUrlFocusChangeAnimations;
+    private boolean mIsMovingNewTabPageView;
 
     /** Flag used to request some layout changes after the next layout pass is completed. */
     private boolean mTileCountChanged;
@@ -190,7 +191,7 @@
                 // was dismissed, avoid also manipulating its vertical offset in our scroll handling
                 // at the same time. The onScrolled() method is called when an item is dismissed and
                 // the item at the top of the viewport is repositioned.
-                if (holder.itemView == mNewTabPageLayout) setUrlFocusAnimationsDisabled(true);
+                if (holder.itemView == mNewTabPageLayout) mIsMovingNewTabPageView = true;
 
                 // Cancel any pending scroll update handling, a new one will be scheduled in
                 // onAnimationFinished().
@@ -210,7 +211,7 @@
                 // from here, as the RecyclerView will animate multiple items when one is dismissed,
                 // and some will "finish" synchronously if they are already in the correct place,
                 // before other moves have even been scheduled.
-                if (viewHolder.itemView == mNewTabPageLayout) setUrlFocusAnimationsDisabled(false);
+                if (viewHolder.itemView == mNewTabPageLayout) mIsMovingNewTabPageView = false;
                 mRecyclerView.removeCallbacks(mUpdateSearchBoxOnScrollRunnable);
                 mRecyclerView.post(mUpdateSearchBoxOnScrollRunnable);
             }
@@ -401,7 +402,7 @@
     }
 
     private void updateSearchBoxOnScroll() {
-        if (mDisableUrlFocusChangeAnimations) return;
+        if (mDisableUrlFocusChangeAnimations || mIsMovingNewTabPageView) return;
 
         // When the page changes (tab switching or new page loading), it is possible that events
         // (e.g. delayed RecyclerView change notifications) trigger calls to these methods after
@@ -632,7 +633,10 @@
     }
 
     private void onUrlFocusAnimationChanged() {
-        if (mDisableUrlFocusChangeAnimations || FeatureUtilities.isChromeHomeEnabled()) return;
+        if (mDisableUrlFocusChangeAnimations || FeatureUtilities.isChromeHomeEnabled()
+                || mIsMovingNewTabPageView) {
+            return;
+        }
 
         // Translate so that the search box is at the top, but only upwards.
         float percent = mSearchProviderHasLogo ? mUrlFocusChangePercent : 0;
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 2dad1b2..a5dc53af 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
@@ -205,14 +205,15 @@
         // from moving later.
         setDefaultFaviconOnView();
         try {
+            long faviconFetchStartTimeMs = SystemClock.elapsedRealtime();
             URI pageUrl = new URI(mArticle.mUrl);
             if (!article.isArticle() || !SnippetsConfig.isFaviconsFromNewServerEnabled()) {
                 // The old code path. Remove when the experiment is successful.
                 // Currently, we have to use this for non-articles, due to privacy.
-                fetchFaviconFromLocalCache(pageUrl, true);
+                fetchFaviconFromLocalCache(pageUrl, true, faviconFetchStartTimeMs);
             } else {
                 // The new code path.
-                fetchFaviconFromLocalCacheOrGoogleServer();
+                fetchFaviconFromLocalCacheOrGoogleServer(faviconFetchStartTimeMs);
             }
         } catch (URISyntaxException e) {
             // Do nothing, stick to the default favicon.
@@ -402,27 +403,52 @@
         transitionDrawable.startTransition(FADE_IN_ANIMATION_TIME_MS);
     }
 
-    private void fetchFaviconFromLocalCacheOrGoogleServer() {
+    private void fetchFaviconFromLocalCacheOrGoogleServer(final long faviconFetchStartTimeMs) {
         // Set the desired size to 0 to specify we do not want to resize in c++, we'll resize here.
         mUiDelegate.getSuggestionsSource().fetchSuggestionFavicon(mArticle,
                 PUBLISHER_FAVICON_MINIMUM_SIZE_PX, /* desiredSizePx */ 0, new Callback<Bitmap>() {
                     @Override
                     public void onResult(Bitmap image) {
+                        recordFaviconFetchTime(faviconFetchStartTimeMs);
                         if (image == null) return;
                         setFaviconOnView(image);
                     }
                 });
     }
 
-    private void fetchFaviconFromLocalCache(final URI snippetUri, final boolean fallbackToService) {
+    private void recordFaviconFetchTime(long faviconFetchStartTimeMs) {
+        RecordHistogram.recordMediumTimesHistogram(
+                "NewTabPage.ContentSuggestions.ArticleFaviconFetchTime",
+                SystemClock.elapsedRealtime() - faviconFetchStartTimeMs, TimeUnit.MILLISECONDS);
+    }
+
+    private void recordFaviconFetchResult(
+            @FaviconFetchResult int result, long faviconFetchStartTimeMs) {
+        // Record the histogram for articles only to have a fair comparision.
+        if (!mArticle.isArticle()) return;
+        RecordHistogram.recordEnumeratedHistogram(
+                "NewTabPage.ContentSuggestions.ArticleFaviconFetchResult", result,
+                FaviconFetchResult.COUNT);
+        recordFaviconFetchTime(faviconFetchStartTimeMs);
+    }
+
+    private void fetchFaviconFromLocalCache(final URI snippetUri, final boolean fallbackToService,
+            final long faviconFetchStartTimeMs) {
         mUiDelegate.getLocalFaviconImageForURL(
                 getSnippetDomain(snippetUri), mPublisherFaviconSizePx, new FaviconImageCallback() {
                     @Override
                     public void onFaviconAvailable(Bitmap image, String iconUrl) {
                         if (image != null) {
                             setFaviconOnView(image);
+                            recordFaviconFetchResult(fallbackToService
+                                            ? FaviconFetchResult.SUCCESS_CACHED
+                                            : FaviconFetchResult.SUCCESS_FETCHED,
+                                    faviconFetchStartTimeMs);
                         } else if (fallbackToService) {
-                            fetchFaviconFromService(snippetUri);
+                            if (!fetchFaviconFromService(snippetUri, faviconFetchStartTimeMs)) {
+                                recordFaviconFetchResult(
+                                        FaviconFetchResult.FAILURE, faviconFetchStartTimeMs);
+                            }
                         }
                         // Else do nothing, we already have the placeholder set.
                     }
@@ -431,10 +457,11 @@
 
     // TODO(crbug.com/635567): Fix this properly.
     @SuppressLint("DefaultLocale")
-    private void fetchFaviconFromService(final URI snippetUri) {
-        if (!mUseFaviconService) return;
+    private boolean fetchFaviconFromService(
+            final URI snippetUri, final long faviconFetchStartTimeMs) {
+        if (!mUseFaviconService) return false;
         int sizePx = getFaviconServiceSupportedSize();
-        if (sizePx == 0) return;
+        if (sizePx == 0) return false;
 
         // Replace the default icon by another one from the service when it is fetched.
         mUiDelegate.ensureIconIsAvailable(
@@ -443,11 +470,17 @@
                 /*useLargeIcon=*/false, /*isTemporary=*/true, new IconAvailabilityCallback() {
                     @Override
                     public void onIconAvailabilityChecked(boolean newlyAvailable) {
-                        if (!newlyAvailable) return;
+                        if (!newlyAvailable) {
+                            recordFaviconFetchResult(
+                                    FaviconFetchResult.FAILURE, faviconFetchStartTimeMs);
+                            return;
+                        }
                         // The download succeeded, the favicon is in the cache; fetch it.
-                        fetchFaviconFromLocalCache(snippetUri, /*fallbackToService=*/false);
+                        fetchFaviconFromLocalCache(
+                                snippetUri, /*fallbackToService=*/false, faviconFetchStartTimeMs);
                     }
                 });
+        return true;
     }
 
     private int getFaviconServiceSupportedSize() {
diff --git a/chrome/app/nibs/OWNERS b/chrome/app/nibs/OWNERS
index f40e0c9..5a19c18 100644
--- a/chrome/app/nibs/OWNERS
+++ b/chrome/app/nibs/OWNERS
@@ -1 +1,2 @@
+set noparent
 file://chrome/browser/ui/cocoa/OWNERS
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index 97f28975..ab942d6 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -3483,6 +3483,11 @@
       "repost_form_warning_controller.h",
       "search/local_ntp_source.cc",
       "search/local_ntp_source.h",
+      "search/one_google_bar/one_google_bar_data.cc",
+      "search/one_google_bar/one_google_bar_data.h",
+      "search/one_google_bar/one_google_bar_fetcher.h",
+      "search/one_google_bar/one_google_bar_fetcher_impl.cc",
+      "search/one_google_bar/one_google_bar_fetcher_impl.h",
       "signin/signin_promo.cc",
       "signin/signin_promo.h",
       "signin/signin_ui_util.cc",
diff --git a/chrome/browser/android/vr_shell/vr_gl_thread.cc b/chrome/browser/android/vr_shell/vr_gl_thread.cc
index e5555f6..0697b1d 100644
--- a/chrome/browser/android/vr_shell/vr_gl_thread.cc
+++ b/chrome/browser/android/vr_shell/vr_gl_thread.cc
@@ -44,6 +44,8 @@
 
 void VrGLThread::CleanUp() {
   vr_shell_gl_.reset();
+  scene_manager_.reset();
+  scene_.reset();
 }
 
 }  // namespace vr_shell
diff --git a/chrome/browser/android/vr_shell/vr_shell_gl.cc b/chrome/browser/android/vr_shell/vr_shell_gl.cc
index 5736237..81397edd8 100644
--- a/chrome/browser/android/vr_shell/vr_shell_gl.cc
+++ b/chrome/browser/android/vr_shell/vr_shell_gl.cc
@@ -727,25 +727,37 @@
       // Once the user starts scrolling send all the scroll events to this
       // element until the scrolling stops.
       case WebInputEvent::kGestureScrollBegin:
-        current_scroll_target = input_target;
-        if (current_scroll_target != InputTarget::NONE) {
-          SendGesture(current_scroll_target, std::move(movableGesture));
+        current_scroll_target_ = input_target;
+        if (current_scroll_target_ != InputTarget::NONE) {
+          SendGesture(current_scroll_target_, std::move(movableGesture));
         }
         break;
       case WebInputEvent::kGestureScrollEnd:
-        if (current_scroll_target != InputTarget::NONE) {
-          SendGesture(current_scroll_target, std::move(movableGesture));
+        if (current_scroll_target_ != InputTarget::NONE) {
+          SendGesture(current_scroll_target_, std::move(movableGesture));
         }
-        current_scroll_target = InputTarget::NONE;
+        current_fling_target_ = current_scroll_target_;
+        current_scroll_target_ = InputTarget::NONE;
         break;
       case WebInputEvent::kGestureScrollUpdate:
-      case WebInputEvent::kGestureFlingCancel:
+        if (current_scroll_target_ != InputTarget::NONE) {
+          SendGesture(current_scroll_target_, std::move(movableGesture));
+        }
+        break;
       case WebInputEvent::kGestureFlingStart:
-        if (current_scroll_target != InputTarget::NONE) {
-          SendGesture(current_scroll_target, std::move(movableGesture));
+        if (current_fling_target_ != InputTarget::NONE) {
+          SendGesture(current_fling_target_, std::move(movableGesture));
+        }
+        current_fling_target_ = InputTarget::NONE;
+        break;
+      case WebInputEvent::kGestureFlingCancel:
+        current_fling_target_ = InputTarget::NONE;
+        if (input_target != InputTarget::NONE) {
+          SendGesture(input_target, std::move(movableGesture));
         }
         break;
       case WebInputEvent::kGestureTapDown:
+        current_fling_target_ = InputTarget::NONE;
         if (input_target != InputTarget::NONE) {
           SendGesture(input_target, std::move(movableGesture));
         }
diff --git a/chrome/browser/android/vr_shell/vr_shell_gl.h b/chrome/browser/android/vr_shell/vr_shell_gl.h
index 15606b999..2d14b66 100644
--- a/chrome/browser/android/vr_shell/vr_shell_gl.h
+++ b/chrome/browser/android/vr_shell/vr_shell_gl.h
@@ -200,7 +200,8 @@
   gfx::Point3F target_point_;
   const UiElement* target_element_ = nullptr;
   InputTarget current_input_target_ = InputTarget::NONE;
-  InputTarget current_scroll_target = InputTarget::NONE;
+  InputTarget current_scroll_target_ = InputTarget::NONE;
+  InputTarget current_fling_target_ = InputTarget::NONE;
   int ui_tex_css_width_ = 0;
   int ui_tex_css_height_ = 0;
   int content_tex_css_width_ = 0;
diff --git a/chrome/browser/autofill/autofill_interactive_uitest.cc b/chrome/browser/autofill/autofill_interactive_uitest.cc
index 4074995..155f9bf 100644
--- a/chrome/browser/autofill/autofill_interactive_uitest.cc
+++ b/chrome/browser/autofill/autofill_interactive_uitest.cc
@@ -764,10 +764,12 @@
 
 // Test that an input field is not rendered with the yellow autofilled
 // background color when choosing an option from the datalist suggestion list.
-#if defined(OS_MACOSX) || defined(OS_CHROMEOS)
+#if defined(OS_MACOSX) || defined(OS_CHROMEOS) || defined(OS_WIN) || \
+    defined(OS_LINUX)
 // Flakily triggers and assert on Mac; flakily gets empty string instead
 // of "Adam" on ChromeOS.
 // http://crbug.com/419868, http://crbug.com/595385.
+// Flaky on Windows and Linux as well: http://crbug.com/595385
 #define MAYBE_OnSelectOptionFromDatalist DISABLED_OnSelectOptionFromDatalist
 #else
 #define MAYBE_OnSelectOptionFromDatalist OnSelectOptionFromDatalist
diff --git a/chrome/browser/chrome_browser_main.cc b/chrome/browser/chrome_browser_main.cc
index e8c3638..ce20f98 100644
--- a/chrome/browser/chrome_browser_main.cc
+++ b/chrome/browser/chrome_browser_main.cc
@@ -35,6 +35,7 @@
 #include "base/strings/sys_string_conversions.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/sys_info.h"
+#include "base/task_scheduler/post_task.h"
 #include "base/threading/platform_thread.h"
 #include "base/time/default_tick_clock.h"
 #include "base/time/time.h"
@@ -1706,7 +1707,8 @@
   // Verify that the profile is not on a network share and if so prepare to show
   // notification to the user.
   if (NetworkProfileBubble::ShouldCheckNetworkProfile(profile_)) {
-    BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE,
+    base::PostTaskWithTraits(
+        FROM_HERE, base::TaskTraits().MayBlock(),
         base::Bind(&NetworkProfileBubble::CheckNetworkProfile,
                    profile_->GetPath()));
   }
diff --git a/chrome/browser/chromeos/file_manager/file_manager_browsertest.cc b/chrome/browser/chromeos/file_manager/file_manager_browsertest.cc
index 7179ba56..6f39cd9 100644
--- a/chrome/browser/chromeos/file_manager/file_manager_browsertest.cc
+++ b/chrome/browser/chromeos/file_manager/file_manager_browsertest.cc
@@ -121,13 +121,9 @@
                       "audioRepeatOneModeMultipleFileDrive")));
 
 // Fails on official build. http://crbug.com/429294
-#if defined(DISABLE_SLOW_FILESAPP_TESTS) || defined(OFFICIAL_BUILD)
-#define MAYBE_OpenImageFiles DISABLED_OpenImageFiles
-#else
-#define MAYBE_OpenImageFiles OpenImageFiles
-#endif
+// Flaky: http://crbug.com/711290
 WRAPPED_INSTANTIATE_TEST_CASE_P(
-    MAYBE_OpenImageFiles,
+    DISABLED_OpenImageFiles,
     FileManagerBrowserTest,
     ::testing::Values(TestParameter(IN_GUEST_MODE, "imageOpenDownloads"),
                       TestParameter(NOT_IN_GUEST_MODE, "imageOpenDownloads"),
diff --git a/chrome/browser/search/one_google_bar/one_google_bar_data.cc b/chrome/browser/search/one_google_bar/one_google_bar_data.cc
new file mode 100644
index 0000000..eac69e8
--- /dev/null
+++ b/chrome/browser/search/one_google_bar/one_google_bar_data.cc
@@ -0,0 +1,14 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/search/one_google_bar/one_google_bar_data.h"
+
+OneGoogleBarData::OneGoogleBarData() = default;
+OneGoogleBarData::OneGoogleBarData(const OneGoogleBarData&) = default;
+OneGoogleBarData::OneGoogleBarData(OneGoogleBarData&&) = default;
+OneGoogleBarData::~OneGoogleBarData() = default;
+
+OneGoogleBarData& OneGoogleBarData::operator=(const OneGoogleBarData&) =
+    default;
+OneGoogleBarData& OneGoogleBarData::operator=(OneGoogleBarData&&) = default;
diff --git a/chrome/browser/search/one_google_bar/one_google_bar_data.h b/chrome/browser/search/one_google_bar/one_google_bar_data.h
new file mode 100644
index 0000000..caf3a27
--- /dev/null
+++ b/chrome/browser/search/one_google_bar/one_google_bar_data.h
@@ -0,0 +1,32 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_SEARCH_ONE_GOOGLE_BAR_ONE_GOOGLE_BAR_DATA_H_
+#define CHROME_BROWSER_SEARCH_ONE_GOOGLE_BAR_ONE_GOOGLE_BAR_DATA_H_
+
+#include <string>
+
+// This struct contains all the data needed to inject a OneGoogleBar into a
+// page.
+struct OneGoogleBarData {
+  OneGoogleBarData();
+  OneGoogleBarData(const OneGoogleBarData&);
+  OneGoogleBarData(OneGoogleBarData&&);
+  ~OneGoogleBarData();
+
+  OneGoogleBarData& operator=(const OneGoogleBarData&);
+  OneGoogleBarData& operator=(OneGoogleBarData&&);
+
+  // The main HTML for the bar itself.
+  std::string bar_html;
+
+  // "Page hooks" that need to be inserted at certain points in the page HTML.
+  std::string in_head_script;
+  std::string in_head_style;
+  std::string after_bar_script;
+  std::string end_of_body_html;
+  std::string end_of_body_script;
+};
+
+#endif  // CHROME_BROWSER_SEARCH_ONE_GOOGLE_BAR_ONE_GOOGLE_BAR_DATA_H_
diff --git a/chrome/browser/search/one_google_bar/one_google_bar_fetcher.h b/chrome/browser/search/one_google_bar/one_google_bar_fetcher.h
new file mode 100644
index 0000000..4b42d421
--- /dev/null
+++ b/chrome/browser/search/one_google_bar/one_google_bar_fetcher.h
@@ -0,0 +1,24 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_SEARCH_ONE_GOOGLE_BAR_ONE_GOOGLE_BAR_FETCHER_H_
+#define CHROME_BROWSER_SEARCH_ONE_GOOGLE_BAR_ONE_GOOGLE_BAR_FETCHER_H_
+
+#include "base/callback_forward.h"
+#include "base/optional.h"
+
+struct OneGoogleBarData;
+
+// Interface for fetching OneGoogleBarData over the network.
+class OneGoogleBarFetcher {
+ public:
+  using OneGoogleCallback =
+      base::OnceCallback<void(const base::Optional<OneGoogleBarData>&)>;
+
+  // Initiates a fetch from the network. On completion (successful or not), the
+  // callback will be called with the result, which will be nullopt on failure.
+  virtual void Fetch(OneGoogleCallback callback) = 0;
+};
+
+#endif  // CHROME_BROWSER_SEARCH_ONE_GOOGLE_BAR_ONE_GOOGLE_BAR_FETCHER_H_
diff --git a/chrome/browser/search/one_google_bar/one_google_bar_fetcher_impl.cc b/chrome/browser/search/one_google_bar/one_google_bar_fetcher_impl.cc
new file mode 100644
index 0000000..704f878
--- /dev/null
+++ b/chrome/browser/search/one_google_bar/one_google_bar_fetcher_impl.cc
@@ -0,0 +1,374 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/search/one_google_bar/one_google_bar_fetcher_impl.h"
+
+#include <string>
+#include <utility>
+
+#include "base/callback.h"
+#include "base/command_line.h"
+#include "base/json/json_writer.h"
+#include "base/memory/ptr_util.h"
+#include "base/strings/string_util.h"
+#include "base/strings/stringprintf.h"
+#include "base/values.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/search/one_google_bar/one_google_bar_data.h"
+#include "chrome/common/chrome_content_client.h"
+#include "components/google/core/browser/google_url_tracker.h"
+#include "components/safe_json/safe_json_parser.h"
+#include "components/signin/core/browser/access_token_fetcher.h"
+#include "components/signin/core/browser/signin_manager.h"
+#include "components/variations/net/variations_http_headers.h"
+#include "google_apis/gaia/oauth2_token_service.h"
+#include "google_apis/google_api_keys.h"
+#include "net/base/load_flags.h"
+#include "net/http/http_status_code.h"
+#include "net/url_request/url_fetcher.h"
+#include "net/url_request/url_fetcher_delegate.h"
+
+namespace {
+
+const char kApiUrl[] = "https://onegoogle-pa.googleapis.com/v1/getbar";
+
+const char kApiKeyFormat[] = "?key=%s";
+
+const char kApiScope[] = "https://www.googleapis.com/auth/onegoogle.readonly";
+const char kAuthorizationRequestHeaderFormat[] = "Bearer %s";
+
+const char kResponsePreamble[] = ")]}'";
+
+// This namespace contains helpers to extract SafeHtml-wrapped strings (see
+// https://github.com/google/safe-html-types) from the response json. If there
+// is ever a C++ version of the SafeHtml types, we should consider using that
+// instead of these custom functions.
+namespace safe_html {
+
+bool GetImpl(const base::DictionaryValue& dict,
+             const std::string& name,
+             const std::string& wrapped_field_name,
+             std::string* out) {
+  const base::DictionaryValue* value = nullptr;
+  if (!dict.GetDictionary(name, &value)) {
+    out->clear();
+    return false;
+  }
+
+  if (!value->GetString(wrapped_field_name, out)) {
+    out->clear();
+    return false;
+  }
+
+  return true;
+}
+
+bool GetHtml(const base::DictionaryValue& dict,
+             const std::string& name,
+             std::string* out) {
+  return GetImpl(dict, name, "privateDoNotAccessOrElseSafeHtmlWrappedValue",
+                 out);
+}
+
+bool GetScript(const base::DictionaryValue& dict,
+               const std::string& name,
+               std::string* out) {
+  return GetImpl(dict, name, "privateDoNotAccessOrElseSafeScriptWrappedValue",
+                 out);
+}
+
+bool GetStyleSheet(const base::DictionaryValue& dict,
+                   const std::string& name,
+                   std::string* out) {
+  return GetImpl(dict, name,
+                 "privateDoNotAccessOrElseSafeStyleSheetWrappedValue", out);
+}
+
+}  // namespace safe_html
+
+base::Optional<OneGoogleBarData> JsonToOGBData(const base::Value& value) {
+  const base::DictionaryValue* dict = nullptr;
+  if (!value.GetAsDictionary(&dict)) {
+    DLOG(WARNING) << "Parse error: top-level dictionary not found";
+    return base::nullopt;
+  }
+
+  const base::DictionaryValue* one_google_bar = nullptr;
+  if (!dict->GetDictionary("oneGoogleBar", &one_google_bar)) {
+    DLOG(WARNING) << "Parse error: no oneGoogleBar";
+    return base::nullopt;
+  }
+
+  OneGoogleBarData result;
+
+  if (!safe_html::GetHtml(*one_google_bar, "html", &result.bar_html)) {
+    DLOG(WARNING) << "Parse error: no html";
+    return base::nullopt;
+  }
+
+  const base::DictionaryValue* page_hooks = nullptr;
+  if (!one_google_bar->GetDictionary("pageHooks", &page_hooks)) {
+    DLOG(WARNING) << "Parse error: no pageHooks";
+    return base::nullopt;
+  }
+
+  safe_html::GetScript(*page_hooks, "inHeadScript", &result.in_head_script);
+  safe_html::GetStyleSheet(*page_hooks, "inHeadStyle", &result.in_head_style);
+  safe_html::GetScript(*page_hooks, "afterBarScript", &result.after_bar_script);
+  safe_html::GetHtml(*page_hooks, "endOfBodyHtml", &result.end_of_body_html);
+  safe_html::GetScript(*page_hooks, "endOfBodyScript",
+                       &result.end_of_body_script);
+
+  return result;
+}
+
+}  // namespace
+
+class OneGoogleBarFetcherImpl::AuthenticatedURLFetcher
+    : public net::URLFetcherDelegate {
+ public:
+  using FetchDoneCallback = base::OnceCallback<void(const net::URLFetcher*)>;
+
+  AuthenticatedURLFetcher(SigninManagerBase* signin_manager,
+                          OAuth2TokenService* token_service,
+                          net::URLRequestContextGetter* request_context,
+                          const GURL& google_base_url,
+                          FetchDoneCallback callback);
+  ~AuthenticatedURLFetcher() override = default;
+
+  void Start();
+
+ private:
+  GURL GetApiUrl(bool use_oauth) const;
+  std::string GetRequestBody() const;
+  std::string GetExtraRequestHeaders(const GURL& url,
+                                     const std::string& access_token) const;
+
+  void GotAccessToken(const GoogleServiceAuthError& error,
+                      const std::string& access_token);
+
+  // URLFetcherDelegate implementation.
+  void OnURLFetchComplete(const net::URLFetcher* source) override;
+
+  SigninManagerBase* const signin_manager_;
+  OAuth2TokenService* const token_service_;
+  net::URLRequestContextGetter* const request_context_;
+  const GURL google_base_url_;
+
+  FetchDoneCallback callback_;
+
+  std::unique_ptr<AccessTokenFetcher> token_fetcher_;
+
+  // The underlying URLFetcher which does the actual fetch.
+  std::unique_ptr<net::URLFetcher> url_fetcher_;
+};
+
+OneGoogleBarFetcherImpl::AuthenticatedURLFetcher::AuthenticatedURLFetcher(
+    SigninManagerBase* signin_manager,
+    OAuth2TokenService* token_service,
+    net::URLRequestContextGetter* request_context,
+    const GURL& google_base_url,
+    FetchDoneCallback callback)
+    : signin_manager_(signin_manager),
+      token_service_(token_service),
+      request_context_(request_context),
+      google_base_url_(google_base_url),
+      callback_(std::move(callback)) {}
+
+void OneGoogleBarFetcherImpl::AuthenticatedURLFetcher::Start() {
+  if (!signin_manager_->IsAuthenticated()) {
+    GotAccessToken(GoogleServiceAuthError::AuthErrorNone(), std::string());
+    return;
+  }
+  OAuth2TokenService::ScopeSet scopes;
+  scopes.insert(kApiScope);
+  token_fetcher_ = base::MakeUnique<AccessTokenFetcher>(
+      "one_google", signin_manager_, token_service_, scopes,
+      base::BindOnce(&AuthenticatedURLFetcher::GotAccessToken,
+                     base::Unretained(this)));
+}
+
+GURL OneGoogleBarFetcherImpl::AuthenticatedURLFetcher::GetApiUrl(
+    bool use_oauth) const {
+  std::string api_url(kApiUrl);
+  // TODO(treib): Attach to feature instead of cmdline.
+  base::CommandLine* cmdline = base::CommandLine::ForCurrentProcess();
+  if (cmdline->HasSwitch("one-google-api-url"))
+    api_url = cmdline->GetSwitchValueASCII("one-google-api-url");
+  // Append the API key only for unauthenticated requests.
+  if (!use_oauth) {
+    api_url +=
+        base::StringPrintf(kApiKeyFormat, google_apis::GetAPIKey().c_str());
+  }
+
+  return GURL(api_url);
+}
+
+std::string OneGoogleBarFetcherImpl::AuthenticatedURLFetcher::GetRequestBody()
+    const {
+  // TODO(treib): Attach to feature instead of cmdline.
+  base::CommandLine* cmdline = base::CommandLine::ForCurrentProcess();
+  if (cmdline->HasSwitch("one-google-bar-options"))
+    return cmdline->GetSwitchValueASCII("one-google-bar-options");
+
+  base::DictionaryValue dict;
+  dict.SetInteger("subproduct", 243);
+  dict.SetBoolean("enable_multilogin", true);
+  dict.SetString("user_agent", GetUserAgent());
+  dict.SetString("accept_language", g_browser_process->GetApplicationLocale());
+  dict.SetString("original_request_url", google_base_url_.spec());
+  auto material_options_dict = base::MakeUnique<base::DictionaryValue>();
+  material_options_dict->SetString("page_title", " ");
+  material_options_dict->SetBoolean("position_fixed", true);
+  material_options_dict->SetBoolean("disable_moving_userpanel_to_menu", true);
+  auto styling_options_dict = base::MakeUnique<base::DictionaryValue>();
+  auto background_color_dict = base::MakeUnique<base::DictionaryValue>();
+  auto alpha_dict = base::MakeUnique<base::DictionaryValue>();
+  alpha_dict->SetInteger("value", 0);
+  background_color_dict->Set("alpha", std::move(alpha_dict));
+  styling_options_dict->Set("background_color",
+                            std::move(background_color_dict));
+  material_options_dict->Set("styling_options",
+                             std::move(styling_options_dict));
+  dict.Set("material_options", std::move(material_options_dict));
+
+  std::string result;
+  base::JSONWriter::Write(dict, &result);
+  return result;
+}
+
+std::string
+OneGoogleBarFetcherImpl::AuthenticatedURLFetcher::GetExtraRequestHeaders(
+    const GURL& url,
+    const std::string& access_token) const {
+  net::HttpRequestHeaders headers;
+  headers.SetHeader("Content-Type", "application/json; charset=UTF-8");
+  if (!access_token.empty()) {
+    headers.SetHeader("Authorization",
+                      base::StringPrintf(kAuthorizationRequestHeaderFormat,
+                                         access_token.c_str()));
+  }
+  // Note: It's OK to pass |is_signed_in| false if it's unknown, as it does
+  // not affect transmission of experiments coming from the variations server.
+  variations::AppendVariationHeaders(url,
+                                     /*incognito=*/false, /*uma_enabled=*/false,
+                                     /*is_signed_in=*/false, &headers);
+  return headers.ToString();
+}
+
+void OneGoogleBarFetcherImpl::AuthenticatedURLFetcher::GotAccessToken(
+    const GoogleServiceAuthError& error,
+    const std::string& access_token) {
+  // Delete the token fetcher after we leave this method.
+  std::unique_ptr<AccessTokenFetcher> deleter(std::move(token_fetcher_));
+
+  bool use_oauth = !access_token.empty();
+  GURL url = GetApiUrl(use_oauth);
+  url_fetcher_ = net::URLFetcher::Create(0, url, net::URLFetcher::POST, this);
+  url_fetcher_->SetRequestContext(request_context_);
+
+  url_fetcher_->SetLoadFlags(net::LOAD_DO_NOT_SEND_AUTH_DATA |
+                             net::LOAD_DO_NOT_SEND_COOKIES |
+                             net::LOAD_DO_NOT_SAVE_COOKIES);
+  url_fetcher_->SetUploadData("application/json", GetRequestBody());
+  url_fetcher_->SetExtraRequestHeaders(
+      GetExtraRequestHeaders(url, access_token));
+
+  url_fetcher_->Start();
+}
+
+void OneGoogleBarFetcherImpl::AuthenticatedURLFetcher::OnURLFetchComplete(
+    const net::URLFetcher* source) {
+  std::move(callback_).Run(source);
+}
+
+OneGoogleBarFetcherImpl::OneGoogleBarFetcherImpl(
+    SigninManagerBase* signin_manager,
+    OAuth2TokenService* token_service,
+    net::URLRequestContextGetter* request_context,
+    GoogleURLTracker* google_url_tracker)
+    : signin_manager_(signin_manager),
+      token_service_(token_service),
+      request_context_(request_context),
+      google_url_tracker_(google_url_tracker),
+      weak_ptr_factory_(this) {}
+
+OneGoogleBarFetcherImpl::~OneGoogleBarFetcherImpl() = default;
+
+void OneGoogleBarFetcherImpl::Fetch(OneGoogleCallback callback) {
+  callbacks_.push_back(std::move(callback));
+  IssueRequestIfNoneOngoing();
+}
+
+void OneGoogleBarFetcherImpl::IssueRequestIfNoneOngoing() {
+  // If there is an ongoing request, let it complete.
+  if (pending_request_.get())
+    return;
+
+  pending_request_ = base::MakeUnique<AuthenticatedURLFetcher>(
+      signin_manager_, token_service_, request_context_,
+      google_url_tracker_->google_url(),
+      base::BindOnce(&OneGoogleBarFetcherImpl::FetchDone,
+                     base::Unretained(this)));
+  pending_request_->Start();
+}
+
+void OneGoogleBarFetcherImpl::FetchDone(const net::URLFetcher* source) {
+  // The fetcher will be deleted when the request is handled.
+  std::unique_ptr<AuthenticatedURLFetcher> deleter(std::move(pending_request_));
+
+  const net::URLRequestStatus& request_status = source->GetStatus();
+  if (request_status.status() != net::URLRequestStatus::SUCCESS) {
+    // This represents network errors (i.e. the server did not provide a
+    // response).
+    DLOG(WARNING) << "Request failed with error: " << request_status.error()
+                  << ": " << net::ErrorToString(request_status.error());
+    Respond(base::nullopt);
+    return;
+  }
+
+  const int response_code = source->GetResponseCode();
+  if (response_code != net::HTTP_OK) {
+    DLOG(WARNING) << "Response code: " << response_code;
+    std::string response;
+    source->GetResponseAsString(&response);
+    DLOG(WARNING) << "Response: " << response;
+    Respond(base::nullopt);
+    return;
+  }
+
+  std::string response;
+  bool success = source->GetResponseAsString(&response);
+  DCHECK(success);
+
+  // The response may start with )]}'. Ignore this.
+  if (base::StartsWith(response, kResponsePreamble,
+                       base::CompareCase::SENSITIVE)) {
+    response = response.substr(strlen(kResponsePreamble));
+  }
+
+  safe_json::SafeJsonParser::Parse(
+      response,
+      base::Bind(&OneGoogleBarFetcherImpl::JsonParsed,
+                 weak_ptr_factory_.GetWeakPtr()),
+      base::Bind(&OneGoogleBarFetcherImpl::JsonParseFailed,
+                 weak_ptr_factory_.GetWeakPtr()));
+}
+
+void OneGoogleBarFetcherImpl::JsonParsed(std::unique_ptr<base::Value> value) {
+  Respond(JsonToOGBData(*value));
+}
+
+void OneGoogleBarFetcherImpl::JsonParseFailed(const std::string& message) {
+  DLOG(WARNING) << "Parsing JSON failed: " << message;
+  Respond(base::nullopt);
+}
+
+void OneGoogleBarFetcherImpl::Respond(
+    const base::Optional<OneGoogleBarData>& data) {
+  for (auto& callback : callbacks_) {
+    std::move(callback).Run(data);
+  }
+  callbacks_.clear();
+}
diff --git a/chrome/browser/search/one_google_bar/one_google_bar_fetcher_impl.h b/chrome/browser/search/one_google_bar/one_google_bar_fetcher_impl.h
new file mode 100644
index 0000000..40538ef
--- /dev/null
+++ b/chrome/browser/search/one_google_bar/one_google_bar_fetcher_impl.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 CHROME_BROWSER_SEARCH_ONE_GOOGLE_BAR_ONE_GOOGLE_BAR_FETCHER_IMPL_H_
+#define CHROME_BROWSER_SEARCH_ONE_GOOGLE_BAR_ONE_GOOGLE_BAR_FETCHER_IMPL_H_
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "base/optional.h"
+#include "chrome/browser/search/one_google_bar/one_google_bar_fetcher.h"
+
+class GoogleURLTracker;
+class OAuth2TokenService;
+class SigninManagerBase;
+
+namespace base {
+class Value;
+}
+
+namespace net {
+class URLFetcher;
+class URLRequestContextGetter;
+}  // namespace net
+
+struct OneGoogleBarData;
+
+class OneGoogleBarFetcherImpl : public OneGoogleBarFetcher {
+ public:
+  OneGoogleBarFetcherImpl(SigninManagerBase* signin_manager,
+                          OAuth2TokenService* token_service,
+                          net::URLRequestContextGetter* request_context,
+                          GoogleURLTracker* google_url_tracker);
+  ~OneGoogleBarFetcherImpl();
+
+  void Fetch(OneGoogleCallback callback) override;
+
+ private:
+  class AuthenticatedURLFetcher;
+
+  void IssueRequestIfNoneOngoing();
+
+  void FetchDone(const net::URLFetcher* source);
+
+  void JsonParsed(std::unique_ptr<base::Value> value);
+  void JsonParseFailed(const std::string& message);
+
+  void Respond(const base::Optional<OneGoogleBarData>& data);
+
+  SigninManagerBase* signin_manager_;
+  OAuth2TokenService* token_service_;
+  net::URLRequestContextGetter* request_context_;
+  GoogleURLTracker* google_url_tracker_;
+
+  std::vector<OneGoogleCallback> callbacks_;
+  std::unique_ptr<AuthenticatedURLFetcher> pending_request_;
+
+  base::WeakPtrFactory<OneGoogleBarFetcherImpl> weak_ptr_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(OneGoogleBarFetcherImpl);
+};
+
+#endif  // CHROME_BROWSER_SEARCH_ONE_GOOGLE_BAR_ONE_GOOGLE_BAR_FETCHER_IMPL_H_
diff --git a/chrome/browser/search/one_google_bar/one_google_bar_fetcher_impl_unittest.cc b/chrome/browser/search/one_google_bar/one_google_bar_fetcher_impl_unittest.cc
new file mode 100644
index 0000000..7e05f845
--- /dev/null
+++ b/chrome/browser/search/one_google_bar/one_google_bar_fetcher_impl_unittest.cc
@@ -0,0 +1,291 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/search/one_google_bar/one_google_bar_fetcher_impl.h"
+
+#include "base/macros.h"
+#include "base/memory/ptr_util.h"
+#include "base/memory/ref_counted.h"
+#include "base/message_loop/message_loop.h"
+#include "base/run_loop.h"
+#include "base/test/mock_callback.h"
+#include "base/test/test_simple_task_runner.h"
+#include "base/time/time.h"
+#include "chrome/browser/search/one_google_bar/one_google_bar_data.h"
+#include "components/google/core/browser/google_url_tracker.h"
+#include "components/safe_json/testing_json_parser.h"
+#include "components/signin/core/browser/account_tracker_service.h"
+#include "components/signin/core/browser/fake_profile_oauth2_token_service.h"
+#include "components/signin/core/browser/fake_signin_manager.h"
+#include "components/signin/core/browser/test_signin_client.h"
+#include "components/sync_preferences/testing_pref_service_syncable.h"
+#include "google_apis/gaia/fake_oauth2_token_service_delegate.h"
+#include "net/http/http_request_headers.h"
+#include "net/http/http_status_code.h"
+#include "net/url_request/test_url_fetcher_factory.h"
+#include "net/url_request/url_request_status.h"
+#include "net/url_request/url_request_test_util.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using testing::_;
+using testing::Eq;
+using testing::IsEmpty;
+using testing::SaveArg;
+using testing::StartsWith;
+
+namespace {
+
+const char kMinimalValidResponse[] = R"json({"oneGoogleBar": {
+  "html": { "privateDoNotAccessOrElseSafeHtmlWrappedValue": "" },
+  "pageHooks": {}
+}})json";
+
+// Required to instantiate a GoogleUrlTracker in UNIT_TEST_MODE.
+class GoogleURLTrackerClientStub : public GoogleURLTrackerClient {
+ public:
+  GoogleURLTrackerClientStub() {}
+  ~GoogleURLTrackerClientStub() override {}
+
+  bool IsBackgroundNetworkingEnabled() override { return true; }
+  PrefService* GetPrefs() override { return nullptr; }
+  net::URLRequestContextGetter* GetRequestContext() override { return nullptr; }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(GoogleURLTrackerClientStub);
+};
+
+}  // namespace
+
+class OneGoogleBarFetcherImplTest : public testing::Test {
+ public:
+  OneGoogleBarFetcherImplTest()
+      : signin_client_(&pref_service_),
+        signin_manager_(&signin_client_, &account_tracker_),
+        task_runner_(new base::TestSimpleTaskRunner()),
+        request_context_getter_(
+            new net::TestURLRequestContextGetter(task_runner_)),
+        token_service_(base::MakeUnique<FakeOAuth2TokenServiceDelegate>(
+            request_context_getter_.get())),
+        google_url_tracker_(base::MakeUnique<GoogleURLTrackerClientStub>(),
+                            GoogleURLTracker::UNIT_TEST_MODE),
+        one_google_bar_fetcher_(&signin_manager_,
+                                &token_service_,
+                                request_context_getter_.get(),
+                                &google_url_tracker_) {
+    SigninManagerBase::RegisterProfilePrefs(pref_service_.registry());
+    SigninManagerBase::RegisterPrefs(pref_service_.registry());
+  }
+
+  void SignIn() {
+    signin_manager_.SignIn("account");
+    token_service_.GetDelegate()->UpdateCredentials("account", "refresh_token");
+  }
+
+  void IssueAccessToken() {
+    token_service_.IssueAllTokensForAccount(
+        "account", "access_token",
+        base::Time::Now() + base::TimeDelta::FromHours(1));
+  }
+
+  void IssueAccessTokenError() {
+    token_service_.IssueErrorForAllPendingRequestsForAccount(
+        "account",
+        GoogleServiceAuthError(GoogleServiceAuthError::SERVICE_UNAVAILABLE));
+  }
+
+  net::TestURLFetcher* GetRunningURLFetcher() {
+    // All created URLFetchers have ID 0 by default.
+    net::TestURLFetcher* url_fetcher = url_fetcher_factory_.GetFetcherByID(0);
+    DCHECK(url_fetcher);
+    return url_fetcher;
+  }
+
+  void RespondWithData(const std::string& data) {
+    net::TestURLFetcher* url_fetcher = GetRunningURLFetcher();
+    url_fetcher->set_status(net::URLRequestStatus());
+    url_fetcher->set_response_code(net::HTTP_OK);
+    url_fetcher->SetResponseString(data);
+    url_fetcher->delegate()->OnURLFetchComplete(url_fetcher);
+    // SafeJsonParser is asynchronous.
+    base::RunLoop().RunUntilIdle();
+  }
+
+  OneGoogleBarFetcherImpl* one_google_bar_fetcher() {
+    return &one_google_bar_fetcher_;
+  }
+
+ private:
+  // variations::AppendVariationHeaders and SafeJsonParser require a
+  // ThreadTaskRunnerHandle to be set.
+  base::MessageLoop message_loop_;
+
+  safe_json::TestingJsonParser::ScopedFactoryOverride factory_override_;
+
+  net::TestURLFetcherFactory url_fetcher_factory_;
+  sync_preferences::TestingPrefServiceSyncable pref_service_;
+
+  TestSigninClient signin_client_;
+  AccountTrackerService account_tracker_;
+  FakeSigninManagerBase signin_manager_;
+
+  scoped_refptr<base::TestSimpleTaskRunner> task_runner_;
+  scoped_refptr<net::TestURLRequestContextGetter> request_context_getter_;
+  FakeProfileOAuth2TokenService token_service_;
+  GoogleURLTracker google_url_tracker_;
+
+  OneGoogleBarFetcherImpl one_google_bar_fetcher_;
+};
+
+TEST_F(OneGoogleBarFetcherImplTest, UnauthenticatedRequestReturns) {
+  base::MockCallback<OneGoogleBarFetcher::OneGoogleCallback> callback;
+  one_google_bar_fetcher()->Fetch(callback.Get());
+
+  base::Optional<OneGoogleBarData> data;
+  EXPECT_CALL(callback, Run(_)).WillOnce(SaveArg<0>(&data));
+  RespondWithData(kMinimalValidResponse);
+
+  EXPECT_TRUE(data.has_value());
+}
+
+TEST_F(OneGoogleBarFetcherImplTest, AuthenticatedRequestReturns) {
+  SignIn();
+
+  base::MockCallback<OneGoogleBarFetcher::OneGoogleCallback> callback;
+  one_google_bar_fetcher()->Fetch(callback.Get());
+
+  IssueAccessToken();
+
+  base::Optional<OneGoogleBarData> data;
+  EXPECT_CALL(callback, Run(_)).WillOnce(SaveArg<0>(&data));
+  RespondWithData(kMinimalValidResponse);
+
+  EXPECT_TRUE(data.has_value());
+}
+
+TEST_F(OneGoogleBarFetcherImplTest, UnauthenticatedRequestHasApiKey) {
+  base::MockCallback<OneGoogleBarFetcher::OneGoogleCallback> callback;
+  one_google_bar_fetcher()->Fetch(callback.Get());
+
+  // The request should have an API key (as a query param).
+  EXPECT_THAT(GetRunningURLFetcher()->GetOriginalURL().query(),
+              StartsWith("key="));
+
+  // But no "Authorization" header.
+  net::HttpRequestHeaders headers;
+  GetRunningURLFetcher()->GetExtraRequestHeaders(&headers);
+  EXPECT_FALSE(headers.HasHeader("Authorization"));
+}
+
+TEST_F(OneGoogleBarFetcherImplTest, AuthenticatedRequestHasAuthHeader) {
+  SignIn();
+
+  base::MockCallback<OneGoogleBarFetcher::OneGoogleCallback> callback;
+  one_google_bar_fetcher()->Fetch(callback.Get());
+
+  IssueAccessToken();
+
+  // The request should *not* have an API key (as a query param).
+  EXPECT_THAT(GetRunningURLFetcher()->GetOriginalURL().query(), IsEmpty());
+
+  // It should have an "Authorization" header.
+  net::HttpRequestHeaders headers;
+  GetRunningURLFetcher()->GetExtraRequestHeaders(&headers);
+  EXPECT_TRUE(headers.HasHeader("Authorization"));
+}
+
+TEST_F(OneGoogleBarFetcherImplTest,
+       AuthenticatedRequestFallsBackToUnauthenticated) {
+  SignIn();
+
+  base::MockCallback<OneGoogleBarFetcher::OneGoogleCallback> callback;
+  one_google_bar_fetcher()->Fetch(callback.Get());
+
+  IssueAccessTokenError();
+
+  // The request should have fallen back to unauthenticated mode with an API key
+  // (as a query param).
+  EXPECT_THAT(GetRunningURLFetcher()->GetOriginalURL().query(),
+              StartsWith("key="));
+
+  // But no "Authorization" header.
+  net::HttpRequestHeaders headers;
+  GetRunningURLFetcher()->GetExtraRequestHeaders(&headers);
+  EXPECT_FALSE(headers.HasHeader("Authorization"));
+}
+
+TEST_F(OneGoogleBarFetcherImplTest, HandlesResponsePreamble) {
+  base::MockCallback<OneGoogleBarFetcher::OneGoogleCallback> callback;
+  one_google_bar_fetcher()->Fetch(callback.Get());
+
+  // The reponse may contain a ")]}'" prefix. The fetcher should ignore that
+  // during parsing.
+  base::Optional<OneGoogleBarData> data;
+  EXPECT_CALL(callback, Run(_)).WillOnce(SaveArg<0>(&data));
+  RespondWithData(std::string(")]}'") + kMinimalValidResponse);
+
+  EXPECT_TRUE(data.has_value());
+}
+
+TEST_F(OneGoogleBarFetcherImplTest, ParsesFullResponse) {
+  base::MockCallback<OneGoogleBarFetcher::OneGoogleCallback> callback;
+  one_google_bar_fetcher()->Fetch(callback.Get());
+
+  base::Optional<OneGoogleBarData> data;
+  EXPECT_CALL(callback, Run(_)).WillOnce(SaveArg<0>(&data));
+  RespondWithData(R"json({"oneGoogleBar": {
+    "html": { "privateDoNotAccessOrElseSafeHtmlWrappedValue": "bar_html" },
+    "pageHooks": {
+      "inHeadScript": {
+        "privateDoNotAccessOrElseSafeScriptWrappedValue": "in_head_script"
+      },
+      "inHeadStyle": {
+        "privateDoNotAccessOrElseSafeStyleSheetWrappedValue": "in_head_style"
+      },
+      "afterBarScript": {
+        "privateDoNotAccessOrElseSafeScriptWrappedValue": "after_bar_script"
+      },
+      "endOfBodyHtml": {
+        "privateDoNotAccessOrElseSafeHtmlWrappedValue": "end_of_body_html"
+      },
+      "endOfBodyScript": {
+        "privateDoNotAccessOrElseSafeScriptWrappedValue": "end_of_body_script"
+      }
+    }
+  }})json");
+
+  ASSERT_TRUE(data.has_value());
+  EXPECT_THAT(data->bar_html, Eq("bar_html"));
+  EXPECT_THAT(data->in_head_script, Eq("in_head_script"));
+  EXPECT_THAT(data->in_head_style, Eq("in_head_style"));
+  EXPECT_THAT(data->after_bar_script, Eq("after_bar_script"));
+  EXPECT_THAT(data->end_of_body_html, Eq("end_of_body_html"));
+  EXPECT_THAT(data->end_of_body_script, Eq("end_of_body_script"));
+}
+
+TEST_F(OneGoogleBarFetcherImplTest, CoalescesMultipleRequests) {
+  // Trigger two requests.
+  base::MockCallback<OneGoogleBarFetcher::OneGoogleCallback> first_callback;
+  one_google_bar_fetcher()->Fetch(first_callback.Get());
+  net::URLFetcher* first_fetcher = GetRunningURLFetcher();
+  base::MockCallback<OneGoogleBarFetcher::OneGoogleCallback> second_callback;
+  one_google_bar_fetcher()->Fetch(second_callback.Get());
+  net::URLFetcher* second_fetcher = GetRunningURLFetcher();
+
+  // Expect that only one fetcher handles both requests.
+  EXPECT_THAT(first_fetcher, Eq(second_fetcher));
+
+  // But both callbacks should get called.
+  base::Optional<OneGoogleBarData> first_data;
+  base::Optional<OneGoogleBarData> second_data;
+
+  EXPECT_CALL(first_callback, Run(_)).WillOnce(SaveArg<0>(&first_data));
+  EXPECT_CALL(second_callback, Run(_)).WillOnce(SaveArg<0>(&second_data));
+
+  RespondWithData(kMinimalValidResponse);
+
+  // Ensure that both requests received a response.
+  EXPECT_TRUE(first_data.has_value());
+  EXPECT_TRUE(second_data.has_value());
+}
diff --git a/chrome/browser/ui/network_profile_bubble.cc b/chrome/browser/ui/network_profile_bubble.cc
index e9d5997..5b02603 100644
--- a/chrome/browser/ui/network_profile_bubble.cc
+++ b/chrome/browser/ui/network_profile_bubble.cc
@@ -90,7 +90,6 @@
 // static
 void NetworkProfileBubble::CheckNetworkProfile(
     const base::FilePath& profile_folder) {
-  DCHECK_CURRENTLY_ON(content::BrowserThread::FILE);
   // On Windows notify the users if their profiles are located on a network
   // share as we don't officially support this setup yet.
   // However we don't want to bother users on Cytrix setups as those have no
diff --git a/chrome/browser/win/jumplist.cc b/chrome/browser/win/jumplist.cc
index 071087a..d9122ccf 100644
--- a/chrome/browser/win/jumplist.cc
+++ b/chrome/browser/win/jumplist.cc
@@ -231,11 +231,10 @@
 }
 
 // Updates the jumplist, once all the data has been fetched.
-void RunUpdateJumpListUserVisiblePriority(
-    IncognitoModePrefs::Availability incognito_availability,
-    const std::wstring& app_id,
-    const base::FilePath& icon_dir,
-    base::RefCountedData<JumpListData>* ref_counted_data) {
+void RunUpdateJumpList(IncognitoModePrefs::Availability incognito_availability,
+                       const std::wstring& app_id,
+                       const base::FilePath& icon_dir,
+                       base::RefCountedData<JumpListData>* ref_counted_data) {
   JumpListData* data = &ref_counted_data->data;
   ShellLinkItemList local_most_visited_pages;
   ShellLinkItemList local_recently_closed_pages;
@@ -508,7 +507,7 @@
 
   if (!waiting_for_icons) {
     // No more favicons are needed by the application JumpList. Schedule a
-    // RunUpdateJumpListUserVisiblePriority call.
+    // RunUpdateJumpList call.
     PostRunUpdate();
     return;
   }
@@ -534,7 +533,7 @@
     JumpListData* data = &jumplist_data_->data;
     base::AutoLock auto_lock(data->list_lock_);
     // Attach the received data to the ShellLinkItem object.
-    // This data will be decoded by the RunUpdateJumpListUserVisiblePriority
+    // This data will be decoded by the RunUpdateJumpList
     // method.
     if (!image_result.image.IsEmpty() && !data->icon_urls_.empty() &&
         data->icon_urls_.front().second.get()) {
@@ -596,15 +595,13 @@
   // Post a task to delete the content in JumpListIcons folder and log the
   // results to UMA.
   update_jumplisticons_task_runner_->PostTask(
-      FROM_HERE,
-      base::Bind(&DeleteDirectoryContentAndLogResultsUserVisiblePriority,
-                 icon_dir_, kFileDeleteLimit));
+      FROM_HERE, base::Bind(&DeleteDirectoryContentAndLogResults, icon_dir_,
+                            kFileDeleteLimit));
 
   // Post a task to update the jumplist used by the shell.
   update_jumplisticons_task_runner_->PostTask(
-      FROM_HERE,
-      base::Bind(&RunUpdateJumpListUserVisiblePriority, incognito_availability,
-                 app_id_, icon_dir_, base::RetainedRef(jumplist_data_)));
+      FROM_HERE, base::Bind(&RunUpdateJumpList, incognito_availability, app_id_,
+                            icon_dir_, base::RetainedRef(jumplist_data_)));
 
   // Post a task to delete JumpListIconsOld folder and log the results to UMA.
   base::FilePath icon_dir_old = icon_dir_.DirName().Append(
diff --git a/chrome/browser/win/jumplist.h b/chrome/browser/win/jumplist.h
index a66dbafd..0279af2 100644
--- a/chrome/browser/win/jumplist.h
+++ b/chrome/browser/win/jumplist.h
@@ -152,7 +152,7 @@
   // Helper for RunUpdate() that determines its parameters.
   void PostRunUpdate();
 
-  // Called on a timer to invoke RunUpdateJumpListUserVisiblePriority() after
+  // Called on a timer to invoke RunUpdateJumpList() after
   // requests storms have subsided.
   void DeferredRunUpdate();
 
diff --git a/chrome/browser/win/jumplist_file_util.cc b/chrome/browser/win/jumplist_file_util.cc
index 720896b..7c98704 100644
--- a/chrome/browser/win/jumplist_file_util.cc
+++ b/chrome/browser/win/jumplist_file_util.cc
@@ -123,9 +123,8 @@
                             dir_status, DIRECTORY_STATUS_END);
 }
 
-void DeleteDirectoryContentAndLogResultsUserVisiblePriority(
-    const base::FilePath& path,
-    int max_file_deleted) {
+void DeleteDirectoryContentAndLogResults(const base::FilePath& path,
+                                         int max_file_deleted) {
   DirectoryStatus dir_status = NON_EXIST;
 
   // Delete the content in |path|. If |path| doesn't exist, create one.
diff --git a/chrome/browser/win/jumplist_file_util.h b/chrome/browser/win/jumplist_file_util.h
index cd51de5..f9ab7a8 100644
--- a/chrome/browser/win/jumplist_file_util.h
+++ b/chrome/browser/win/jumplist_file_util.h
@@ -79,8 +79,7 @@
                                   int max_file_deleted);
 
 // Deletes the content in the directory at |path| and records the result to UMA.
-void DeleteDirectoryContentAndLogResultsUserVisiblePriority(
-    const base::FilePath& path,
-    int max_file_deleted);
+void DeleteDirectoryContentAndLogResults(const base::FilePath& path,
+                                         int max_file_deleted);
 
 #endif  // CHROME_BROWSER_WIN_JUMPLIST_FILE_UTIL_H_
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index 80abde9b..0448b032 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -3671,6 +3671,7 @@
       "../browser/search/instant_service_unittest.cc",
       "../browser/search/instant_unittest_base.cc",
       "../browser/search/instant_unittest_base.h",
+      "../browser/search/one_google_bar/one_google_bar_fetcher_impl_unittest.cc",
       "../browser/search/search_unittest.cc",
       "../browser/sessions/persistent_tab_restore_service_unittest.cc",
       "../browser/signin/mutable_profile_oauth2_token_service_delegate_unittest.cc",
diff --git a/chrome/test/chromedriver/js/focus.js b/chrome/test/chromedriver/js/focus.js
index 20b48db..7030796b 100644
--- a/chrome/test/chromedriver/js/focus.js
+++ b/chrome/test/chromedriver/js/focus.js
@@ -44,7 +44,7 @@
   // concerned, the shadow host is the active element. We need to go through the
   // tree of shadow DOMs to check that the element we gave focus to is now
   // active.
-  if (element != activeElement) {
+  if (element != activeElement && !element.contains(activeElement)) {
     var shadowRoot = activeElement.shadowRoot;
     while (shadowRoot) {
       var activeElement = shadowRoot.activeElement;
@@ -57,6 +57,6 @@
       shadowRoot = activeElement.shadowRoot;
     }
   }
-  if (element != activeElement)
+  if (element != activeElement && !element.contains(activeElement))
     throw new Error('cannot focus element');
 }
diff --git a/chrome/test/data/webui/settings/easy_unlock_browsertest_chromeos.js b/chrome/test/data/webui/settings/easy_unlock_browsertest_chromeos.js
index b9b1a1ae..5e31e69 100644
--- a/chrome/test/data/webui/settings/easy_unlock_browsertest_chromeos.js
+++ b/chrome/test/data/webui/settings/easy_unlock_browsertest_chromeos.js
@@ -30,15 +30,11 @@
 
 // Times out on debug builders and may time out on memory bots because
 // the Settings page can take several seconds to load in a Release build
-// and several times that in a Debug build. See https://crbug.com/558434.
-GEN('#if defined(MEMORY_SANITIZER) || !defined(NDEBUG)');
-GEN('#define MAYBE_EasyUnlock DISABLED_EasyUnlock');
-GEN('#else');
-GEN('#define MAYBE_EasyUnlock EasyUnlock');
-GEN('#endif');
+// and several times that in a Debug build. See https://crbug.com/558434
+// and http://crbug.com/711256.
 
 // Runs easy unlock tests.
-TEST_F('SettingsEasyUnlockBrowserTest', 'MAYBE_EasyUnlock', function() {
+TEST_F('SettingsEasyUnlockBrowserTest', 'DISABLED_EasyUnlock', function() {
   /**
    * A test version of EasyUnlockBrowserProxy. Provides helper methods
    * for allowing tests to know when a method was called, as well as
diff --git a/chrome/test/data/webui/settings/settings_ui_browsertest.js b/chrome/test/data/webui/settings/settings_ui_browsertest.js
index 849b4ac..be2201c 100644
--- a/chrome/test/data/webui/settings/settings_ui_browsertest.js
+++ b/chrome/test/data/webui/settings/settings_ui_browsertest.js
@@ -18,14 +18,10 @@
 
 // Times out on debug builders and may time out on memory bots because
 // the Settings page can take several seconds to load in a Release build
-// and several times that in a Debug build. See https://crbug.com/558434.
-GEN('#if defined(MEMORY_SANITIZER) || !defined(NDEBUG)');
-GEN('#define MAYBE_All DISABLED_All');
-GEN('#else');
-GEN('#define MAYBE_All All');
-GEN('#endif');
+// and several times that in a Debug build. See https://crbug.com/558434
+// and http://crbug.com/711256.
 
-TEST_F('SettingsUIBrowserTest', 'MAYBE_All', function() {
+TEST_F('SettingsUIBrowserTest', 'DISABLED_All', function() {
   suite('settings-ui', function() {
     var toolbar;
     var ui;
diff --git a/chrome/test/nacl/pnacl_header_test.cc b/chrome/test/nacl/pnacl_header_test.cc
index c465bc51..f270f3c 100644
--- a/chrome/test/nacl/pnacl_header_test.cc
+++ b/chrome/test/nacl/pnacl_header_test.cc
@@ -134,7 +134,8 @@
   return std::move(http_response);
 }
 
-IN_PROC_BROWSER_TEST_F(PnaclHeaderTest, TestHasPnaclHeader) {
+// Flaky: http://crbug.com/711289
+IN_PROC_BROWSER_TEST_F(PnaclHeaderTest, DISABLED_TestHasPnaclHeader) {
   // Load 2 pexes, one same origin and one cross orgin.
   RunLoadTest("/nacl/pnacl_request_header/pnacl_request_header.html", 1, 1);
 }
diff --git a/components/ntp_snippets/BUILD.gn b/components/ntp_snippets/BUILD.gn
index 257a1132..0cf1874a 100644
--- a/components/ntp_snippets/BUILD.gn
+++ b/components/ntp_snippets/BUILD.gn
@@ -133,6 +133,7 @@
       "category.h",
       "category_info.h",
       "category_status.h",
+      "content_suggestions_service.cc",
     ]
   }
 }
diff --git a/components/ntp_snippets/content_suggestions_service.cc b/components/ntp_snippets/content_suggestions_service.cc
index d8220e04..861a08e 100644
--- a/components/ntp_snippets/content_suggestions_service.cc
+++ b/components/ntp_snippets/content_suggestions_service.cc
@@ -12,6 +12,7 @@
 #include "base/bind.h"
 #include "base/location.h"
 #include "base/memory/ptr_util.h"
+#include "base/metrics/histogram_macros.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "base/time/default_clock.h"
@@ -26,6 +27,27 @@
 
 namespace ntp_snippets {
 
+namespace {
+
+// Enumeration listing all possible outcomes for fetch attempts of favicons for
+// content suggestions. Used for UMA histograms, so do not change existing
+// values. Insert new values at the end, and update the histogram definition.
+// GENERATED_JAVA_ENUM_PACKAGE: org.chromium.chrome.browser.ntp.snippets
+enum class FaviconFetchResult {
+  SUCCESS_CACHED = 0,
+  SUCCESS_FETCHED = 1,
+  FAILURE = 2,
+  COUNT = 3
+};
+
+void RecordFaviconFetchResult(FaviconFetchResult result) {
+  UMA_HISTOGRAM_ENUMERATION(
+      "NewTabPage.ContentSuggestions.ArticleFaviconFetchResult", result,
+      FaviconFetchResult::COUNT);
+}
+
+}  // namespace
+
 ContentSuggestionsService::ContentSuggestionsService(
     State state,
     SigninManagerBase* signin_manager,
@@ -148,6 +170,7 @@
   if (position == suggestions->end() || !large_icon_service_) {
     base::ThreadTaskRunnerHandle::Get()->PostTask(
         FROM_HERE, base::Bind(callback, gfx::Image()));
+    RecordFaviconFetchResult(FaviconFetchResult::FAILURE);
     return;
   }
 
@@ -175,6 +198,11 @@
     const favicon_base::LargeIconImageResult& result) {
   if (!result.image.IsEmpty()) {
     callback.Run(result.image);
+    // The icon is from cache if we haven't gone to Google server yet. The icon
+    // is freshly fetched, otherwise.
+    RecordFaviconFetchResult(continue_to_google_server
+                                 ? FaviconFetchResult::SUCCESS_CACHED
+                                 : FaviconFetchResult::SUCCESS_FETCHED);
     return;
   }
 
@@ -182,8 +210,10 @@
       (result.fallback_icon_style &&
        !result.fallback_icon_style->is_default_background_color)) {
     // We cannot download from the server if there is some small icon in the
-    // cache (resulting in non-default bakground color) or if we already did so.
+    // cache (resulting in non-default background color) or if we already did
+    // so.
     callback.Run(gfx::Image());
+    RecordFaviconFetchResult(FaviconFetchResult::FAILURE);
     return;
   }
 
@@ -205,6 +235,7 @@
     bool success) {
   if (!success) {
     callback.Run(gfx::Image());
+    RecordFaviconFetchResult(FaviconFetchResult::FAILURE);
     return;
   }
 
diff --git a/components/ntp_snippets/remote/remote_suggestions_scheduler_impl.cc b/components/ntp_snippets/remote/remote_suggestions_scheduler_impl.cc
index e83531a6..82af8ed 100644
--- a/components/ntp_snippets/remote/remote_suggestions_scheduler_impl.cc
+++ b/components/ntp_snippets/remote/remote_suggestions_scheduler_impl.cc
@@ -57,10 +57,10 @@
 // The values of each array specify a default time interval for the intervals
 // defined by the enum FetchingInterval. The default time intervals defined in
 // the arrays can be overridden using different variation parameters.
-const double kDefaultFetchingIntervalHoursRareNtpUser[] = {48.0, 24.0, 12.0,
-                                                           6.0};
-const double kDefaultFetchingIntervalHoursActiveNtpUser[] = {24.0, 6.0, 4.0,
-                                                             2.0};
+const double kDefaultFetchingIntervalHoursRareNtpUser[] = {48.0, 24.0, 8.0,
+                                                           4.0};
+const double kDefaultFetchingIntervalHoursActiveNtpUser[] = {24.0, 8.0, 6.0,
+                                                             3.0};
 const double kDefaultFetchingIntervalHoursActiveSuggestionsConsumer[] = {
     24.0, 6.0, 2.0, 1.0};
 
@@ -579,8 +579,7 @@
 
 std::set<RemoteSuggestionsSchedulerImpl::TriggerType>
 RemoteSuggestionsSchedulerImpl::GetDefaultEnabledTriggerTypes() {
-  return {TriggerType::PERSISTENT_SCHEDULER_WAKE_UP, TriggerType::NTP_OPENED,
-          TriggerType::BROWSER_FOREGROUNDED};
+  return {TriggerType::PERSISTENT_SCHEDULER_WAKE_UP, TriggerType::NTP_OPENED};
 }
 
 }  // namespace ntp_snippets
diff --git a/components/ntp_snippets/remote/remote_suggestions_scheduler_impl_unittest.cc b/components/ntp_snippets/remote/remote_suggestions_scheduler_impl_unittest.cc
index 220863e1..9e8b447 100644
--- a/components/ntp_snippets/remote/remote_suggestions_scheduler_impl_unittest.cc
+++ b/components/ntp_snippets/remote/remote_suggestions_scheduler_impl_unittest.cc
@@ -632,7 +632,7 @@
   // Pretend we are on WiFi (already done in ctor, we make it explicit here).
   EXPECT_CALL(*persistent_scheduler(), IsOnUnmeteredConnection())
       .WillRepeatedly(Return(true));
-  // UserClassifier defaults to UserClass::ACTIVE_NTP_USER which uses a 2h time
+  // UserClassifier defaults to UserClass::ACTIVE_NTP_USER which uses a 3h time
   // interval by default for soft background fetches on WiFi.
 
   // Initial scheduling after being enabled.
@@ -649,11 +649,11 @@
   signal_fetch_done.Run(Status::Success());
 
   // Open NTP again after too short delay. This time no fetch is executed.
-  test_clock()->Advance(base::TimeDelta::FromMinutes(20));
+  test_clock()->Advance(base::TimeDelta::FromMinutes(30));
   scheduler()->OnNTPOpened();
 
   // Open NTP after another delay, now together long enough to issue a fetch.
-  test_clock()->Advance(base::TimeDelta::FromMinutes(100));
+  test_clock()->Advance(base::TimeDelta::FromMinutes(150));
   EXPECT_CALL(*provider(), RefetchInTheBackground(_));
   scheduler()->OnNTPOpened();
 }
@@ -696,7 +696,7 @@
   // Pretend we are not on wifi -> fallback connection.
   EXPECT_CALL(*persistent_scheduler(), IsOnUnmeteredConnection())
       .WillRepeatedly(Return(false));
-  // UserClassifier defaults to UserClass::ACTIVE_NTP_USER which uses a 4h time
+  // UserClassifier defaults to UserClass::ACTIVE_NTP_USER which uses a 6h time
   // interval by default for soft background fetches not on WiFi.
 
   // Initial scheduling after being enabled.
@@ -713,7 +713,7 @@
   signal_fetch_done.Run(Status::Success());
 
   // Open NTP again after too short delay. This time no fetch is executed.
-  test_clock()->Advance(base::TimeDelta::FromMinutes(180));
+  test_clock()->Advance(base::TimeDelta::FromMinutes(300));
   scheduler()->OnNTPOpened();
 
   // Open NTP after another delay, now together long enough to issue a fetch.
diff --git a/components/policy/resources/policy_templates.json b/components/policy/resources/policy_templates.json
index 4fe31eb..fc4f164 100644
--- a/components/policy/resources/policy_templates.json
+++ b/components/policy/resources/policy_templates.json
@@ -8145,11 +8145,11 @@
       },
       'example_value': True,
       'id': 276,
-      'caption': '''Enable add person in profile manager''',
+      'caption': '''Enable add person in user manager''',
       'tags': [],
       'desc': '''If this policy is set to true or not configured, <ph name="PRODUCT_NAME">$1<ex>Google Chrome</ex></ph> will allow Add Person from the user manager.
 
-      If this policy is set to false, <ph name="PRODUCT_NAME">$1<ex>Google Chrome</ex></ph> will not allow creation of new profiles from the profile manager.''',
+      If this policy is set to false, <ph name="PRODUCT_NAME">$1<ex>Google Chrome</ex></ph> will not allow creation of new profiles from the user manager.''',
     },
     {
       'name': 'ForceBrowserSignin',
diff --git a/content/browser/media/media_internals.cc b/content/browser/media/media_internals.cc
index c8b7da3..a43f919 100644
--- a/content/browser/media/media_internals.cc
+++ b/content/browser/media/media_internals.cc
@@ -29,7 +29,7 @@
 #include "media/base/media_log_event.h"
 #include "media/filters/gpu_video_decoder.h"
 
-#if !defined(OS_ANDROID)
+#if !defined(DISABLE_FFMPEG_VIDEO_DECODERS)
 #include "media/filters/decrypting_video_decoder.h"
 #endif
 
@@ -487,7 +487,7 @@
     return uma_name + "Other";
   }
 
-#if !defined(OS_ANDROID)
+#if !defined(DISABLE_FFMPEG_VIDEO_DECODERS)
   if (player_info.video_decoder ==
       media::DecryptingVideoDecoder::kDecoderName) {
     return uma_name + "DVD";
diff --git a/content/public/browser/browser_thread.h b/content/public/browser/browser_thread.h
index ad2db91..fe82220 100644
--- a/content/public/browser/browser_thread.h
+++ b/content/public/browser/browser_thread.h
@@ -74,8 +74,11 @@
     // DEPRECATED: prefer base/task_scheduler/post_task.h for new classes
     // requiring a background file I/O task runner, i.e.:
     //   base::CreateSequencedTaskRunnerWithTraits(
-    //       TaskTraits().MayBlock()
-    //           .WithPriority(TaskPriority::BACKGROUND|USER_VISIBLE)...)
+    //       base::TaskTraits().MayBlock()
+    //           .WithPriority(base::TaskPriority::BACKGROUND))
+    //   Note: You can use base::TaskPriority::USER_VISIBLE instead of
+    //         base::TaskPriority::BACKGROUND if the latency of this operation
+    //         is visible but non-blocking to the user.
     FILE,
 
     // Used for file system operations that block user interactions.
@@ -83,8 +86,8 @@
     // DEPRECATED: prefer base/task_scheduler/post_task.h for new classes
     // requiring a user-blocking file I/O task runner, i.e.:
     //   base::CreateSequencedTaskRunnerWithTraits(
-    //       TaskTraits().MayBlock()
-    //           .WithPriority(TaskPriority::USER_BLOCKING)...)
+    //       base::TaskTraits().MayBlock()
+    //           .WithPriority(base::TaskPriority::USER_BLOCKING))
     FILE_USER_BLOCKING,
 
     // Used to launch and terminate Chrome processes.
@@ -167,7 +170,7 @@
   // DEPRECATED: use base/task_scheduler/post_task.h instead.
   //   * BrowserThread::PostBlockingPoolTask(AndReply)(...) =>
   //         base::PostTaskWithTraits(AndReply)(
-  //             FROM_HERE, TaskTraits().MayBlock()...)
+  //             FROM_HERE, base::TaskTraits().MayBlock()...)
   //   * BrowserThread::PostBlockingPoolSequencedTask =>
   //         Share a single SequencedTaskRunner created via
   //         base::CreateSequencedTaskRunnerWithTraits() instead of sharing a
@@ -223,7 +226,8 @@
   //   BrowserThread::GetBlockingPool()->GetSequencedTaskRunner(
   //       base::SequencedWorkerPool::GetSequenceToken())
   //  =>
-  //   base::CreateSequencedTaskRunnerWithTraits(TaskTraits().MayBlock()...).
+  //   base::CreateSequencedTaskRunnerWithTraits(
+  //       base::TaskTraits().MayBlock()...).
   static base::SequencedWorkerPool* GetBlockingPool() WARN_UNUSED_RESULT;
 
   // Callable on any thread.  Returns whether the given well-known thread is
diff --git a/content/test/gpu/gpu_tests/gpu_process_integration_test.py b/content/test/gpu/gpu_tests/gpu_process_integration_test.py
index 207bbad..ccc6cea1 100644
--- a/content/test/gpu/gpu_tests/gpu_process_integration_test.py
+++ b/content/test/gpu/gpu_tests/gpu_process_integration_test.py
@@ -299,12 +299,12 @@
       # browser into a state where it won't launch.
       return
     elif sys.platform in ('cygwin', 'win32'):
-      # Hit id 34 from kSoftwareRenderingListJson.
+      # Hit id 34 from kSoftwareRenderingListEntries.
       self.RestartBrowserIfNecessaryWithArgs([
         '--gpu-testing-vendor-id=0x5333',
         '--gpu-testing-device-id=0x8811'])
     elif sys.platform.startswith('linux'):
-      # Hit id 50 from kSoftwareRenderingListJson.
+      # Hit id 50 from kSoftwareRenderingListEntries.
       self.RestartBrowserIfNecessaryWithArgs([
         '--gpu-no-complete-info-collection',
         '--gpu-testing-vendor-id=0x10de',
@@ -313,7 +313,7 @@
         '--gpu-testing-gl-renderer=softpipe',
         '--gpu-testing-gl-version="2.1 Mesa 10.1"'])
     elif sys.platform == 'darwin':
-      # Hit id 112 from kSoftwareRenderingListJson.
+      # Hit id 112 from kSoftwareRenderingListEntries.
       self.RestartBrowserIfNecessaryWithArgs([
         '--gpu-testing-vendor-id=0x8086',
         '--gpu-testing-device-id=0x0116'])
@@ -333,7 +333,7 @@
     options = self.__class__._original_finder_options.browser_options
     is_platform_android = options.browser_type.startswith('android')
     if sys.platform.startswith('linux') and not is_platform_android:
-      # Hit id 110 from kSoftwareRenderingListJson.
+      # Hit id 110 from kSoftwareRenderingListEntries.
       self.RestartBrowserIfNecessaryWithArgs([
         '--gpu-testing-vendor-id=0x10de',
         '--gpu-testing-device-id=0x0de1',
@@ -362,13 +362,13 @@
   def _GpuProcess_driver_bug_workarounds_upon_gl_renderer(self, test_path):
     is_platform_android = self._RunningOnAndroid()
     if is_platform_android:
-      # Hit id 108 from kGpuDriverBugListJson.
+      # Hit id 108 from kGpuDriverBugListEntries.
       self.RestartBrowserIfNecessaryWithArgs([
         '--gpu-testing-gl-vendor=NVIDIA Corporation',
         '--gpu-testing-gl-renderer=NVIDIA Tegra',
         '--gpu-testing-gl-version=OpenGL ES 3.1 NVIDIA 343.00'])
     elif sys.platform in ('cygwin', 'win32'):
-      # Hit id 51 and 87 from kGpuDriverBugListJson.
+      # Hit id 51 and 87 from kGpuDriverBugListEntries.
       self.RestartBrowserIfNecessaryWithArgs([
         '--gpu-testing-vendor-id=0x1002',
         '--gpu-testing-device-id=0x6779',
@@ -378,7 +378,7 @@
         '(AMD Radeon HD 6450 Direct3D11 vs_5_0 ps_5_0)',
         '--gpu-testing-gl-version=OpenGL ES 2.0 (ANGLE 2.1.0.0c0d8006a9dd)'])
     elif sys.platform.startswith('linux'):
-      # Hit id 40 from kGpuDriverBugListJson.
+      # Hit id 40 from kGpuDriverBugListEntries.
       self.RestartBrowserIfNecessaryWithArgs([
         '--gpu-testing-vendor-id=0x0101',
         '--gpu-testing-device-id=0x0102',
@@ -414,10 +414,10 @@
                     '--disable-gpu-driver-bug-workarounds']
     # Inject some info to make sure the flags above are effective.
     if sys.platform == 'darwin':
-      # Hit id 33 from kGpuDriverBugListJson.
+      # Hit id 33 from kGpuDriverBugListEntries.
       browser_args.extend(['--gpu-testing-gl-vendor=Imagination'])
     else:
-      # Hit id 5 from kGpuDriverBugListJson.
+      # Hit id 5 from kGpuDriverBugListEntries.
       browser_args.extend(['--gpu-testing-vendor-id=0x10de',
                            '--gpu-testing-device-id=0x0001'])
       # no multi gpu on Android.
@@ -518,7 +518,7 @@
       [])
 
   def _GpuProcess_disabling_workarounds_works(self, test_path):
-    # Hit exception from id 215 from kGpuDriverBugListJson.
+    # Hit exception from id 215 from kGpuDriverBugListEntries.
     self.RestartBrowserIfNecessaryWithArgs([
       '--gpu-testing-vendor-id=0xbad9',
       '--gpu-testing-device-id=0xbad9',
@@ -539,32 +539,38 @@
     # process. On Windows, and eventually on other platforms where
     # SwiftShader is used, this test should pass.
     #
-    # TODO(kbr): figure out a better way than --disable-gpu to
-    # reliably trigger SwiftShader.
-    self.RestartBrowserIfNecessaryWithArgs(['--disable-gpu'])
-    self._NavigateAndWait(test_path)
-    # It looks like when SwiftShader is in use (via --disable-gpu),
-    # that GPU information collection doesn't yet contain what's
-    # expected (the system_info.gpu.aux_attributes['gl_renderer']
-    # looks like it'll be null). Verified locally that we can fetch
-    # the desired information via WebGL.
-    renderer = self.tab.EvaluateJavaScript('gl_renderer')
-    if not renderer:
-      self.fail('getParameter(UNMASKED_RENDERER_WEBGL) was null')
-    if 'SwiftShader' not in renderer:
-      self.fail('Expected SwiftShader renderer; instead got ' + renderer)
-    if not self.browser.supports_system_info:
-      self.fail("Browser doesn't support GetSystemInfo")
-    gpu = self.browser.GetSystemInfo().gpu
-    if not gpu:
-      self.fail('Target machine must have a GPU')
-    if not gpu.aux_attributes:
-      self.fail('Browser must support GPU aux attributes')
-    if not gpu.aux_attributes['software_rendering']:
-      self.fail("Software rendering was disabled")
-    device = gpu.devices[0]
-    if not device:
-      self.fail("System Info doesn't have a device")
+    args_list = ([
+      # Hit id 4 from kSoftwareRenderingListEntries.
+      '--gpu-testing-vendor-id=0x8086',
+      '--gpu-testing-device-id=0x27A2'],
+      # Explicitly disable GPU access.
+     ['--disable-gpu'])
+    for args in args_list:
+      self.RestartBrowserIfNecessaryWithArgs(args)
+      self._NavigateAndWait(test_path)
+      # Validate the WebGL unmasked renderer string.
+      renderer = self.tab.EvaluateJavaScript('gl_renderer')
+      if not renderer:
+        self.fail('getParameter(UNMASKED_RENDERER_WEBGL) was null')
+      if 'SwiftShader' not in renderer:
+        self.fail('Expected SwiftShader renderer; instead got ' + renderer)
+      # Validate GPU info.
+      if not self.browser.supports_system_info:
+        self.fail("Browser doesn't support GetSystemInfo")
+      gpu = self.browser.GetSystemInfo().gpu
+      if not gpu:
+        self.fail('Target machine must have a GPU')
+      if not gpu.aux_attributes:
+        self.fail('Browser must support GPU aux attributes')
+      if not gpu.aux_attributes['software_rendering']:
+        self.fail("Software rendering was disabled")
+      if 'SwiftShader' not in gpu.aux_attributes['gl_renderer']:
+        self.fail("Expected 'SwiftShader' in GPU info GL renderer string")
+      if 'Google' not in gpu.aux_attributes['gl_vendor']:
+        self.fail("Expected 'Google' in GPU info GL vendor string")
+      device = gpu.devices[0]
+      if not device:
+        self.fail("System Info doesn't have a device")
 
 def load_tests(loader, tests, pattern):
   del loader, tests, pattern  # Unused.
diff --git a/ios/chrome/browser/ui/dialogs/javascript_dialog_egtest.mm b/ios/chrome/browser/ui/dialogs/javascript_dialog_egtest.mm
index 5e81cc9..2c7e0f42 100644
--- a/ios/chrome/browser/ui/dialogs/javascript_dialog_egtest.mm
+++ b/ios/chrome/browser/ui/dialogs/javascript_dialog_egtest.mm
@@ -555,6 +555,11 @@
                           @"correctly.");
 #endif
 
+  // TODO(crbug.com/711291): reenable this on tablets.
+  if (IsIPadIdiom()) {
+    EARL_GREY_TEST_DISABLED(@"Disabled for iPad.");
+  }
+
   // Load the test page with a link to kOnLoadAlertURL and long tap on the link.
   [self loadPageWithLink];
   id<GREYMatcher> webViewMatcher =
diff --git a/ios/chrome/browser/ui/settings/BUILD.gn b/ios/chrome/browser/ui/settings/BUILD.gn
index efa90a0b..edbc096 100644
--- a/ios/chrome/browser/ui/settings/BUILD.gn
+++ b/ios/chrome/browser/ui/settings/BUILD.gn
@@ -25,7 +25,8 @@
   ]
 }
 
-source_set("settings") {
+source_set("settings_arc") {
+  configs += [ "//build/config/compiler:enable_arc" ]
   sources = [
     "about_chrome_collection_view_controller.h",
     "about_chrome_collection_view_controller.mm",
@@ -48,6 +49,106 @@
     "block_popups_collection_view_controller.mm",
     "clear_browsing_data_collection_view_controller.h",
     "clear_browsing_data_collection_view_controller.mm",
+  ]
+  deps = [
+    ":resources",
+    "//base",
+    "//base:i18n",
+    "//components/autofill/core/browser",
+    "//components/autofill/core/common",
+    "//components/autofill/ios/browser",
+    "//components/browser_sync",
+    "//components/browsing_data/core",
+    "//components/content_settings/core/browser",
+    "//components/content_settings/core/common",
+    "//components/google/core/browser",
+    "//components/handoff",
+    "//components/history/core/browser",
+    "//components/image_fetcher/ios",
+    "//components/keyed_service/core",
+    "//components/metrics",
+    "//components/password_manager/core/browser",
+    "//components/password_manager/core/common",
+    "//components/physical_web/data_source",
+    "//components/prefs",
+    "//components/resources",
+    "//components/search_engines",
+    "//components/signin/core/browser",
+    "//components/signin/core/common",
+    "//components/signin/ios/browser",
+    "//components/strings",
+    "//components/sync",
+    "//components/translate/core/browser",
+    "//components/translate/core/common",
+    "//components/url_formatter",
+    "//components/version_info",
+    "//ios/chrome/app/strings",
+    "//ios/chrome/browser",
+    "//ios/chrome/browser/autofill",
+    "//ios/chrome/browser/autofill:autofill_internal",
+    "//ios/chrome/browser/browser_state",
+    "//ios/chrome/browser/browser_state:browser_state_impl",
+    "//ios/chrome/browser/browsing_data",
+    "//ios/chrome/browser/content_settings",
+    "//ios/chrome/browser/history",
+    "//ios/chrome/browser/native_app_launcher:native_app_launcher_internal",
+    "//ios/chrome/browser/passwords",
+    "//ios/chrome/browser/payments/cells",
+    "//ios/chrome/browser/physical_web",
+    "//ios/chrome/browser/prefs",
+    "//ios/chrome/browser/search_engines",
+    "//ios/chrome/browser/signin",
+    "//ios/chrome/browser/store_kit",
+    "//ios/chrome/browser/sync",
+    "//ios/chrome/browser/translate",
+    "//ios/chrome/browser/ui",
+    "//ios/chrome/browser/ui/alert_coordinator",
+    "//ios/chrome/browser/ui/authentication",
+    "//ios/chrome/browser/ui/authentication:authentication_arc",
+    "//ios/chrome/browser/ui/authentication:authentication_ui",
+    "//ios/chrome/browser/ui/autofill",
+    "//ios/chrome/browser/ui/autofill:autofill_ui",
+    "//ios/chrome/browser/ui/autofill/cells",
+    "//ios/chrome/browser/ui/collection_view",
+    "//ios/chrome/browser/ui/colors",
+    "//ios/chrome/browser/ui/commands",
+    "//ios/chrome/browser/ui/content_suggestions/cells",
+    "//ios/chrome/browser/ui/contextual_search",
+    "//ios/chrome/browser/ui/icons",
+    "//ios/chrome/browser/ui/keyboard",
+    "//ios/chrome/browser/ui/material_components",
+    "//ios/chrome/browser/ui/settings/cells",
+    "//ios/chrome/browser/ui/settings/utils",
+    "//ios/chrome/browser/ui/sync",
+    "//ios/chrome/browser/voice",
+    "//ios/chrome/common",
+    "//ios/public/provider/chrome/browser",
+    "//ios/public/provider/chrome/browser/images",
+    "//ios/public/provider/chrome/browser/native_app_launcher",
+    "//ios/public/provider/chrome/browser/signin",
+    "//ios/public/provider/chrome/browser/user_feedback",
+    "//ios/public/provider/chrome/browser/voice",
+    "//ios/third_party/material_components_ios",
+    "//ios/third_party/material_roboto_font_loader_ios",
+    "//ios/web",
+    "//net",
+    "//ui/base",
+    "//url",
+  ]
+  public_deps = [
+    "//ios/chrome/browser/ui/collection_view/cells",
+  ]
+  allow_circular_includes_from = [ "//ios/chrome/browser/ui/authentication" ]
+  libs = [
+    "CoreLocation.framework",
+    "LocalAuthentication.framework",
+    "StoreKit.framework",
+    "UIKit.framework",
+  ]
+}
+
+source_set("settings") {
+  sources = [
     "content_settings_collection_view_controller.h",
     "content_settings_collection_view_controller.mm",
     "contextual_search_collection_view_controller.h",
@@ -187,9 +288,13 @@
     "//url",
   ]
   public_deps = [
+    ":settings_arc",
     "//ios/chrome/browser/ui/collection_view/cells",
   ]
-  allow_circular_includes_from = [ "//ios/chrome/browser/ui/authentication" ]
+  allow_circular_includes_from = [
+    "//ios/chrome/browser/ui/authentication",
+    ":settings_arc",
+  ]
   libs = [
     "CoreLocation.framework",
     "LocalAuthentication.framework",
diff --git a/ios/chrome/browser/ui/settings/about_chrome_collection_view_controller.mm b/ios/chrome/browser/ui/settings/about_chrome_collection_view_controller.mm
index 11b6ff8..740243f 100644
--- a/ios/chrome/browser/ui/settings/about_chrome_collection_view_controller.mm
+++ b/ios/chrome/browser/ui/settings/about_chrome_collection_view_controller.mm
@@ -7,7 +7,6 @@
 #import "base/ios/block_types.h"
 #include "base/logging.h"
 #import "base/mac/foundation_util.h"
-#import "base/mac/scoped_nsobject.h"
 #include "base/strings/sys_string_conversions.h"
 #include "base/strings/utf_string_conversions.h"
 #include "components/version_info/version_info.h"
@@ -27,6 +26,10 @@
 #include "ui/base/l10n/l10n_util_mac.h"
 #include "url/gurl.h"
 
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
 namespace {
 
 typedef NS_ENUM(NSInteger, SectionIdentifier) {
@@ -64,35 +67,32 @@
 
   [model addSectionWithIdentifier:SectionIdentifierLinks];
 
-  base::scoped_nsobject<CollectionViewTextItem> credits(
-      [[CollectionViewTextItem alloc] initWithType:ItemTypeLinksCredits]);
-  credits.get().text = l10n_util::GetNSString(IDS_IOS_OPEN_SOURCE_LICENSES);
-  credits.get().accessoryType =
-      MDCCollectionViewCellAccessoryDisclosureIndicator;
-  credits.get().accessibilityTraits = UIAccessibilityTraitButton;
+  CollectionViewTextItem* credits =
+      [[CollectionViewTextItem alloc] initWithType:ItemTypeLinksCredits];
+  credits.text = l10n_util::GetNSString(IDS_IOS_OPEN_SOURCE_LICENSES);
+  credits.accessoryType = MDCCollectionViewCellAccessoryDisclosureIndicator;
+  credits.accessibilityTraits = UIAccessibilityTraitButton;
   [model addItem:credits toSectionWithIdentifier:SectionIdentifierLinks];
 
-  base::scoped_nsobject<CollectionViewTextItem> terms(
-      [[CollectionViewTextItem alloc] initWithType:ItemTypeLinksTerms]);
-  terms.get().text = l10n_util::GetNSString(IDS_IOS_TERMS_OF_SERVICE);
-  terms.get().accessoryType = MDCCollectionViewCellAccessoryDisclosureIndicator;
-  terms.get().accessibilityTraits = UIAccessibilityTraitButton;
+  CollectionViewTextItem* terms =
+      [[CollectionViewTextItem alloc] initWithType:ItemTypeLinksTerms];
+  terms.text = l10n_util::GetNSString(IDS_IOS_TERMS_OF_SERVICE);
+  terms.accessoryType = MDCCollectionViewCellAccessoryDisclosureIndicator;
+  terms.accessibilityTraits = UIAccessibilityTraitButton;
   [model addItem:terms toSectionWithIdentifier:SectionIdentifierLinks];
 
-  base::scoped_nsobject<CollectionViewTextItem> privacy(
-      [[CollectionViewTextItem alloc] initWithType:ItemTypeLinksPrivacy]);
-  privacy.get().text = l10n_util::GetNSString(IDS_IOS_PRIVACY_POLICY);
-  privacy.get().accessoryType =
-      MDCCollectionViewCellAccessoryDisclosureIndicator;
-  privacy.get().accessibilityTraits = UIAccessibilityTraitButton;
+  CollectionViewTextItem* privacy =
+      [[CollectionViewTextItem alloc] initWithType:ItemTypeLinksPrivacy];
+  privacy.text = l10n_util::GetNSString(IDS_IOS_PRIVACY_POLICY);
+  privacy.accessoryType = MDCCollectionViewCellAccessoryDisclosureIndicator;
+  privacy.accessibilityTraits = UIAccessibilityTraitButton;
   [model addItem:privacy toSectionWithIdentifier:SectionIdentifierLinks];
 
   [model addSectionWithIdentifier:SectionIdentifierFooter];
 
-  base::scoped_nsobject<VersionItem> version(
-      [[VersionItem alloc] initWithType:ItemTypeVersion]);
-  version.get().text = [self versionDescriptionString];
-  version.get().accessibilityTraits = UIAccessibilityTraitButton;
+  VersionItem* version = [[VersionItem alloc] initWithType:ItemTypeVersion];
+  version.text = [self versionDescriptionString];
+  version.accessibilityTraits = UIAccessibilityTraitButton;
   [model addItem:version toSectionWithIdentifier:SectionIdentifierFooter];
 }
 
diff --git a/ios/chrome/browser/ui/settings/accounts_collection_view_controller.mm b/ios/chrome/browser/ui/settings/accounts_collection_view_controller.mm
index 6cc4d39..9e92d47 100644
--- a/ios/chrome/browser/ui/settings/accounts_collection_view_controller.mm
+++ b/ios/chrome/browser/ui/settings/accounts_collection_view_controller.mm
@@ -4,9 +4,7 @@
 
 #import "ios/chrome/browser/ui/settings/accounts_collection_view_controller.h"
 
-#import "base/ios/weak_nsobject.h"
 #import "base/mac/foundation_util.h"
-#import "base/mac/scoped_nsobject.h"
 #include "base/metrics/user_metrics.h"
 #include "base/strings/sys_string_conversions.h"
 #include "base/strings/utf_string_conversions.h"
@@ -54,6 +52,10 @@
 #import "net/base/mac/url_conversions.h"
 #include "ui/base/l10n/l10n_util_mac.h"
 
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
 NSString* const kSettingsAccountsId = @"kSettingsAccountsId";
 NSString* const kSettingsHeaderId = @"kSettingsHeaderId";
 NSString* const kSettingsAccountsSignoutCellId =
@@ -88,23 +90,21 @@
   BOOL _closeSettingsOnAddAccount;
   std::unique_ptr<SyncObserverBridge> _syncObserver;
   std::unique_ptr<OAuth2TokenServiceObserverBridge> _tokenServiceObserver;
-  base::scoped_nsobject<SigninInteractionController>
-      _signinInteractionController;
+  SigninInteractionController* _signinInteractionController;
   // Modal alert for sign out.
-  base::scoped_nsobject<AlertCoordinator> _alertCoordinator;
+  AlertCoordinator* _alertCoordinator;
   // Whether an authentication operation is in progress (e.g switch accounts,
   // sign out).
   BOOL _authenticationOperationInProgress;
   // Whether the view controller is currently being dismissed and new dismiss
   // requests should be ignored.
   BOOL _isBeingDismissed;
-  base::WeakNSObject<UIViewController> _settingsDetails;
-  base::scoped_nsobject<ResizedAvatarCache> _avatarCache;
+  __weak UIViewController* _settingsDetails;
+  ResizedAvatarCache* _avatarCache;
   std::unique_ptr<ChromeIdentityServiceObserverBridge> _identityServiceObserver;
 
   // Enable lookup of item corresponding to a given identity GAIA ID string.
-  base::scoped_nsobject<NSDictionary<NSString*, CollectionViewItem*>>
-      _identityMap;
+  NSDictionary<NSString*, CollectionViewItem*>* _identityMap;
 }
 
 // Stops observing browser state services. This is required during the shutdown
@@ -139,7 +139,7 @@
                name:kSwitchAccountDidFinishNotification
              object:nil];
     self.collectionViewAccessibilityIdentifier = kSettingsAccountsId;
-    _avatarCache.reset([[ResizedAvatarCache alloc] init]);
+    _avatarCache = [[ResizedAvatarCache alloc] init];
     _identityServiceObserver.reset(
         new ChromeIdentityServiceObserverBridge(self));
     [self loadModel];
@@ -150,7 +150,6 @@
 
 - (void)dealloc {
   [[NSNotificationCenter defaultCenter] removeObserver:self];
-  [super dealloc];
 }
 
 - (void)stopBrowserStateServiceObservers {
@@ -199,7 +198,7 @@
   CollectionViewModel* model = self.collectionViewModel;
 
   NSMutableDictionary<NSString*, CollectionViewItem*>* mutableIdentityMap =
-      [[[NSMutableDictionary alloc] init] autorelease];
+      [[NSMutableDictionary alloc] init];
 
   // Account cells.
   ProfileOAuth2TokenService* oauth2_service =
@@ -219,7 +218,7 @@
 
     [mutableIdentityMap setObject:item forKey:identity.gaiaID];
   }
-  _identityMap.reset([mutableIdentityMap retain]);
+  _identityMap = mutableIdentityMap;
 
   [model addItem:[self addAccountItem]
       toSectionWithIdentifier:SectionIdentifierAccounts];
@@ -239,8 +238,8 @@
 #pragma mark - Model objects
 
 - (CollectionViewItem*)header {
-  CollectionViewTextItem* header = [
-      [[CollectionViewTextItem alloc] initWithType:ItemTypeHeader] autorelease];
+  CollectionViewTextItem* header =
+      [[CollectionViewTextItem alloc] initWithType:ItemTypeHeader];
   header.text = l10n_util::GetNSString(IDS_IOS_OPTIONS_ACCOUNTS_DESCRIPTION);
   header.accessibilityIdentifier = kSettingsHeaderId;
   header.textColor = [[MDCPalette greyPalette] tint500];
@@ -248,8 +247,8 @@
 }
 
 - (CollectionViewItem*)accountItem:(ChromeIdentity*)identity {
-  CollectionViewAccountItem* item = [[[CollectionViewAccountItem alloc]
-      initWithType:ItemTypeAccount] autorelease];
+  CollectionViewAccountItem* item =
+      [[CollectionViewAccountItem alloc] initWithType:ItemTypeAccount];
   [self updateAccountItem:item withIdentity:identity];
   return item;
 }
@@ -263,8 +262,8 @@
 }
 
 - (CollectionViewItem*)addAccountItem {
-  CollectionViewAccountItem* item = [[[CollectionViewAccountItem alloc]
-      initWithType:ItemTypeAddAccount] autorelease];
+  CollectionViewAccountItem* item =
+      [[CollectionViewAccountItem alloc] initWithType:ItemTypeAddAccount];
   item.text =
       l10n_util::GetNSString(IDS_IOS_OPTIONS_ACCOUNTS_ADD_ACCOUNT_BUTTON);
   item.image = [UIImage imageNamed:@"settings_accounts_add_account"];
@@ -273,7 +272,7 @@
 
 - (CollectionViewItem*)syncItem {
   AccountControlItem* item =
-      [[[AccountControlItem alloc] initWithType:ItemTypeSync] autorelease];
+      [[AccountControlItem alloc] initWithType:ItemTypeSync];
   item.text = l10n_util::GetNSString(IDS_IOS_OPTIONS_ACCOUNTS_SYNC_TITLE);
   item.accessibilityIdentifier = kSettingsAccountsSyncCellId;
   [self updateSyncItem:item];
@@ -315,8 +314,8 @@
 }
 
 - (CollectionViewItem*)googleActivityControlsItem {
-  AccountControlItem* item = [[[AccountControlItem alloc]
-      initWithType:ItemTypeGoogleActivityControls] autorelease];
+  AccountControlItem* item =
+      [[AccountControlItem alloc] initWithType:ItemTypeGoogleActivityControls];
   item.text = l10n_util::GetNSString(IDS_IOS_OPTIONS_ACCOUNTS_GOOGLE_TITLE);
   item.detailText =
       l10n_util::GetNSString(IDS_IOS_OPTIONS_ACCOUNTS_GOOGLE_DESCRIPTION);
@@ -328,8 +327,8 @@
 }
 
 - (CollectionViewItem*)signOutItem {
-  CollectionViewTextItem* item = [[[CollectionViewTextItem alloc]
-      initWithType:ItemTypeSignOut] autorelease];
+  CollectionViewTextItem* item =
+      [[CollectionViewTextItem alloc] initWithType:ItemTypeSignOut];
   item.text = l10n_util::GetNSString(IDS_IOS_OPTIONS_ACCOUNTS_SIGNOUT);
   item.accessibilityTraits |= UIAccessibilityTraitButton;
   item.accessibilityIdentifier = kSettingsAccountsSignoutCellId;
@@ -417,7 +416,7 @@
   [self popViewIfSignedOut];
   if (![self authService]->IsAuthenticated() && _settingsDetails) {
     [_settingsDetails dismissViewControllerAnimated:YES completion:nil];
-    _settingsDetails.reset();
+    _settingsDetails = nil;
   }
 }
 
@@ -433,10 +432,10 @@
     return;
   }
 
-  base::scoped_nsobject<UIViewController> controllerToPush(
+  UIViewController* controllerToPush =
       [[SyncSettingsCollectionViewController alloc]
             initWithBrowserState:_browserState
-          allowSwitchSyncAccount:YES]);
+          allowSwitchSyncAccount:YES];
   [self.navigationController pushViewController:controllerToPush animated:YES];
 }
 
@@ -445,14 +444,14 @@
     return;
   base::RecordAction(base::UserMetricsAction(
       "Signin_AccountSettings_GoogleActivityControlsClicked"));
-  base::scoped_nsobject<UINavigationController> settingsDetails(
+  UINavigationController* settingsDetails =
       ios::GetChromeBrowserProvider()
           ->GetChromeIdentityService()
           ->NewWebAndAppSettingDetails(
-              [self authService]->GetAuthenticatedIdentity(), self));
+              [self authService]->GetAuthenticatedIdentity(), self);
   UIImage* closeIcon = [ChromeIcon closeIcon];
   SEL action = @selector(closeGoogleActivitySettings:);
-  [settingsDetails.get().topViewController navigationItem].leftBarButtonItem =
+  [settingsDetails.topViewController navigationItem].leftBarButtonItem =
       [ChromeIcon templateBarButtonItemWithImage:closeIcon
                                           target:self
                                           action:action];
@@ -460,7 +459,7 @@
 
   // Keep a weak reference on the settings details, to be able to dismiss it
   // when the primary account is removed.
-  _settingsDetails.reset(settingsDetails);
+  _settingsDetails = settingsDetails;
 }
 
 - (void)closeGoogleActivitySettings:(id)sender {
@@ -478,17 +477,17 @@
     // in-progress.
     return;
   }
-  _signinInteractionController.reset([[SigninInteractionController alloc]
+  _signinInteractionController = [[SigninInteractionController alloc]
           initWithBrowserState:_browserState
       presentingViewController:self.navigationController
          isPresentedOnSettings:YES
              signInAccessPoint:signin_metrics::AccessPoint::
-                                   ACCESS_POINT_SETTINGS]);
+                                   ACCESS_POINT_SETTINGS];
 
   // |_authenticationOperationInProgress| is reset when the signin interaction
   // controller is dismissed.
   _authenticationOperationInProgress = YES;
-  base::WeakNSObject<AccountsCollectionViewController> weakSelf(self);
+  __weak AccountsCollectionViewController* weakSelf = self;
   [_signinInteractionController addAccountWithCompletion:^(BOOL success) {
     [weakSelf handleDidAddAccount:success];
   }
@@ -496,11 +495,11 @@
 }
 
 - (void)handleDidAddAccount:(BOOL)success {
-  _signinInteractionController.reset();
+  _signinInteractionController = nil;
   [self handleAuthenticationOperationDidFinish];
   if (success && _closeSettingsOnAddAccount) {
-    base::scoped_nsobject<GenericChromeCommand> closeSettingsCommand(
-        [[GenericChromeCommand alloc] initWithTag:IDC_CLOSE_SETTINGS]);
+    GenericChromeCommand* closeSettingsCommand =
+        [[GenericChromeCommand alloc] initWithTag:IDC_CLOSE_SETTINGS];
     [self chromeExecuteCommand:closeSettingsCommand];
   }
 }
@@ -508,10 +507,9 @@
 - (void)showAccountDetails:(ChromeIdentity*)identity {
   if ([_alertCoordinator isVisible])
     return;
-  base::scoped_nsobject<UIViewController> accountDetails(
-      ios::GetChromeBrowserProvider()
-          ->GetChromeIdentityService()
-          ->NewAccountDetails(identity, self));
+  UIViewController* accountDetails = ios::GetChromeBrowserProvider()
+                                         ->GetChromeIdentityService()
+                                         ->NewAccountDetails(identity, self);
   if (!accountDetails) {
     // Failed to create a new account details. Ignored.
     return;
@@ -520,7 +518,7 @@
 
   // Keep a weak reference on the account details, to be able to dismiss it
   // when the primary account is removed.
-  _settingsDetails.reset(accountDetails);
+  _settingsDetails = accountDetails;
 }
 
 - (void)showDisconnect {
@@ -549,15 +547,15 @@
     continueButtonTitle = l10n_util::GetNSString(
         IDS_IOS_DISCONNECT_DIALOG_CONTINUE_BUTTON_MOBILE);
   }
-  _alertCoordinator.reset([[AlertCoordinator alloc]
-      initWithBaseViewController:self
-                           title:title
-                         message:message]);
+  _alertCoordinator =
+      [[AlertCoordinator alloc] initWithBaseViewController:self
+                                                     title:title
+                                                   message:message];
 
   [_alertCoordinator addItemWithTitle:l10n_util::GetNSString(IDS_CANCEL)
                                action:nil
                                 style:UIAlertActionStyleCancel];
-  base::WeakNSObject<AccountsCollectionViewController> weakSelf(self);
+  __weak AccountsCollectionViewController* weakSelf = self;
   [_alertCoordinator addItemWithTitle:continueButtonTitle
                                action:^{
                                  [weakSelf handleDisconnect];
@@ -635,8 +633,8 @@
 - (void)openURL:(NSURL*)url
               view:(UIView*)view
     viewController:(UIViewController*)viewController {
-  base::scoped_nsobject<OpenUrlCommand> command(
-      [[OpenUrlCommand alloc] initWithURLFromChrome:net::GURLWithNSURL(url)]);
+  OpenUrlCommand* command =
+      [[OpenUrlCommand alloc] initWithURLFromChrome:net::GURLWithNSURL(url)];
   [command setTag:IDC_CLOSE_SETTINGS_AND_OPEN_URL];
   [self chromeExecuteCommand:command];
 }
diff --git a/ios/chrome/browser/ui/settings/autofill_collection_view_controller.mm b/ios/chrome/browser/ui/settings/autofill_collection_view_controller.mm
index 13c22d5..9f901101 100644
--- a/ios/chrome/browser/ui/settings/autofill_collection_view_controller.mm
+++ b/ios/chrome/browser/ui/settings/autofill_collection_view_controller.mm
@@ -4,10 +4,7 @@
 
 #import "ios/chrome/browser/ui/settings/autofill_collection_view_controller.h"
 
-#import "base/ios/weak_nsobject.h"
 #include "base/mac/foundation_util.h"
-#import "base/mac/objc_property_releaser.h"
-#import "base/mac/scoped_nsobject.h"
 #include "base/strings/sys_string_conversions.h"
 #include "components/autofill/core/browser/personal_data_manager.h"
 #include "components/autofill/core/common/autofill_pref_names.h"
@@ -28,6 +25,10 @@
 #import "ios/third_party/material_components_ios/src/components/Palettes/src/MaterialPalettes.h"
 #include "ui/base/l10n/l10n_util.h"
 
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
 namespace {
 
 typedef NS_ENUM(NSInteger, SectionIdentifier) {
@@ -52,8 +53,7 @@
     PersonalDataManagerObserverBridgeDelegate> {
   std::string _locale;  // User locale.
   autofill::PersonalDataManager* _personalDataManager;
-  base::mac::ObjCPropertyReleaser
-      _propertyReleaser_AutofillCollectionViewController;
+
   ios::ChromeBrowserState* _browserState;
   std::unique_ptr<autofill::PersonalDataManagerObserverBridge> _observer;
   BOOL _deletionInProgress;
@@ -86,16 +86,12 @@
 
     [self updateEditButton];
     [self loadModel];
-
-    _propertyReleaser_AutofillCollectionViewController.Init(
-        self, [AutofillCollectionViewController class]);
   }
   return self;
 }
 
 - (void)dealloc {
   _personalDataManager->RemoveObserver(_observer.get());
-  [super dealloc];
 }
 
 #pragma mark - CollectionViewController
@@ -154,16 +150,16 @@
 }
 
 - (CollectionViewItem*)autofillSwitchItem {
-  CollectionViewSwitchItem* switchItem = [[[CollectionViewSwitchItem alloc]
-      initWithType:ItemTypeAutofillSwitch] autorelease];
+  CollectionViewSwitchItem* switchItem =
+      [[CollectionViewSwitchItem alloc] initWithType:ItemTypeAutofillSwitch];
   switchItem.text = l10n_util::GetNSString(IDS_IOS_AUTOFILL);
   switchItem.on = [self isAutofillEnabled];
   return switchItem;
 }
 
 - (CollectionViewItem*)walletSwitchItem {
-  CollectionViewSwitchItem* switchItem = [[[CollectionViewSwitchItem alloc]
-      initWithType:ItemTypeWalletSwitch] autorelease];
+  CollectionViewSwitchItem* switchItem =
+      [[CollectionViewSwitchItem alloc] initWithType:ItemTypeWalletSwitch];
   switchItem.text = l10n_util::GetNSString(IDS_IOS_AUTOFILL_USE_WALLET_DATA);
   switchItem.on = [self isWalletEnabled];
   return switchItem;
@@ -182,8 +178,8 @@
 }
 
 - (CollectionViewTextItem*)genericHeader {
-  CollectionViewTextItem* header = [
-      [[CollectionViewTextItem alloc] initWithType:ItemTypeHeader] autorelease];
+  CollectionViewTextItem* header =
+      [[CollectionViewTextItem alloc] initWithType:ItemTypeHeader];
   header.textColor = [[MDCPalette greyPalette] tint500];
   return header;
 }
@@ -199,7 +195,7 @@
                          autofill::AutofillProfile::SERVER_PROFILE;
 
   AutofillDataItem* item =
-      [[[AutofillDataItem alloc] initWithType:ItemTypeAddress] autorelease];
+      [[AutofillDataItem alloc] initWithType:ItemTypeAddress];
   item.text = title;
   item.leadingDetailText = subTitle;
   item.accessoryType = MDCCollectionViewCellAccessoryDisclosureIndicator;
@@ -218,8 +214,7 @@
   std::string guid(creditCard.guid());
   NSString* creditCardName = autofill::GetCreditCardName(creditCard, _locale);
 
-  AutofillDataItem* item =
-      [[[AutofillDataItem alloc] initWithType:ItemTypeCard] autorelease];
+  AutofillDataItem* item = [[AutofillDataItem alloc] initWithType:ItemTypeCard];
   item.text = creditCardName;
   item.leadingDetailText = autofill::GetCreditCardObfuscatedNumber(creditCard);
   item.accessoryType = MDCCollectionViewCellAccessoryDisclosureIndicator;
@@ -284,11 +279,10 @@
   [self updateEditButton];
 
   // Avoid reference cycle in block.
-  base::WeakNSObject<AutofillCollectionViewController> weakSelf(self);
+  __weak AutofillCollectionViewController* weakSelf = self;
   [self.collectionView performBatchUpdates:^{
     // Obtain strong reference again.
-    base::scoped_nsobject<AutofillCollectionViewController> strongSelf(
-        [weakSelf retain]);
+    AutofillCollectionViewController* strongSelf = weakSelf;
     if (!strongSelf) {
       return;
     }
@@ -380,7 +374,7 @@
 }
 
 - (NSIndexSet*)indexSetForExistingProfileAndCardSections {
-  NSMutableIndexSet* sections = [[[NSMutableIndexSet alloc] init] autorelease];
+  NSMutableIndexSet* sections = [[NSMutableIndexSet alloc] init];
   if ([self.collectionViewModel
           hasSectionForSectionIdentifier:SectionIdentifierProfiles]) {
     [sections
@@ -457,29 +451,29 @@
   }
 
   CollectionViewModel* model = self.collectionViewModel;
-  base::scoped_nsobject<UIViewController> controller;
+  UIViewController* controller;
   switch ([model itemTypeForIndexPath:indexPath]) {
     case ItemTypeAddress: {
       const std::vector<autofill::AutofillProfile*> autofillProfiles =
           _personalDataManager->GetProfiles();
-      controller.reset([[AutofillProfileEditCollectionViewController
+      controller = [AutofillProfileEditCollectionViewController
           controllerWithProfile:*autofillProfiles[indexPath.item]
-            personalDataManager:_personalDataManager] retain]);
+            personalDataManager:_personalDataManager];
       break;
     }
     case ItemTypeCard: {
       const std::vector<autofill::CreditCard*>& creditCards =
           _personalDataManager->GetCreditCards();
-      controller.reset([[AutofillCreditCardEditCollectionViewController alloc]
+      controller = [[AutofillCreditCardEditCollectionViewController alloc]
            initWithCreditCard:*creditCards[indexPath.item]
-          personalDataManager:_personalDataManager]);
+          personalDataManager:_personalDataManager];
       break;
     }
     default:
       break;
   }
 
-  if (controller.get()) {
+  if (controller) {
     [self.navigationController pushViewController:controller animated:YES];
   }
 }
@@ -539,11 +533,10 @@
       [self.collectionViewModel sectionForSectionIdentifier:sectionIdentifier];
   if ([self.collectionView numberOfItemsInSection:section] == 0) {
     // Avoid reference cycle in block.
-    base::WeakNSObject<AutofillCollectionViewController> weakSelf(self);
+    __weak AutofillCollectionViewController* weakSelf = self;
     [self.collectionView performBatchUpdates:^{
       // Obtain strong reference again.
-      base::scoped_nsobject<AutofillCollectionViewController> strongSelf(
-          [weakSelf retain]);
+      AutofillCollectionViewController* strongSelf = weakSelf;
       if (!strongSelf) {
         return;
       }
@@ -556,8 +549,7 @@
     }
         completion:^(BOOL finished) {
           // Obtain strong reference again.
-          base::scoped_nsobject<AutofillCollectionViewController> strongSelf(
-              [weakSelf retain]);
+          AutofillCollectionViewController* strongSelf = weakSelf;
           if (!strongSelf) {
             return;
           }
@@ -567,7 +559,7 @@
             [[strongSelf editor] setEditing:NO];
           }
           [strongSelf updateEditButton];
-          strongSelf.get()->_deletionInProgress = NO;
+          strongSelf->_deletionInProgress = NO;
         }];
   }
 }
diff --git a/ios/chrome/browser/ui/settings/autofill_credit_card_edit_collection_view_controller.mm b/ios/chrome/browser/ui/settings/autofill_credit_card_edit_collection_view_controller.mm
index f17c319..ce984d06 100644
--- a/ios/chrome/browser/ui/settings/autofill_credit_card_edit_collection_view_controller.mm
+++ b/ios/chrome/browser/ui/settings/autofill_credit_card_edit_collection_view_controller.mm
@@ -6,10 +6,8 @@
 
 #include "base/format_macros.h"
 #import "base/ios/block_types.h"
-#import "base/ios/weak_nsobject.h"
 #import "base/mac/foundation_util.h"
 #include "base/mac/scoped_block.h"
-#import "base/mac/scoped_nsobject.h"
 #include "base/strings/sys_string_conversions.h"
 #include "components/autofill/core/browser/autofill_data_util.h"
 #include "components/autofill/core/browser/credit_card.h"
@@ -31,9 +29,14 @@
 #import "ios/chrome/browser/ui/uikit_ui_util.h"
 #include "ios/chrome/grit/ios_chromium_strings.h"
 #include "ios/chrome/grit/ios_strings.h"
+#import "ios/third_party/material_components_ios/src/components/Buttons/src/MaterialButtons.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "url/gurl.h"
 
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
 namespace {
 using ::AutofillTypeFromAutofillUIType;
 
@@ -89,8 +92,8 @@
   if (_creditCard.record_type() == autofill::CreditCard::FULL_SERVER_CARD ||
       _creditCard.record_type() == autofill::CreditCard::MASKED_SERVER_CARD) {
     GURL paymentsURL = autofill::payments::GetManageInstrumentsUrl(0);
-    base::scoped_nsobject<OpenUrlCommand> command(
-        [[OpenUrlCommand alloc] initWithURLFromChrome:paymentsURL]);
+    OpenUrlCommand* command =
+        [[OpenUrlCommand alloc] initWithURLFromChrome:paymentsURL];
     [command setTag:IDC_CLOSE_SETTINGS_AND_OPEN_URL];
     [self chromeExecuteCommand:command];
 
@@ -141,8 +144,8 @@
   BOOL isEditing = self.editor.editing;
 
   [model addSectionWithIdentifier:SectionIdentifierFields];
-  AutofillEditItem* cardholderNameitem = [[[AutofillEditItem alloc]
-      initWithType:ItemTypeCardholderName] autorelease];
+  AutofillEditItem* cardholderNameitem =
+      [[AutofillEditItem alloc] initWithType:ItemTypeCardholderName];
   cardholderNameitem.textFieldName =
       l10n_util::GetNSString(IDS_IOS_AUTOFILL_CARDHOLDER);
   cardholderNameitem.textFieldValue = autofill::GetCreditCardName(
@@ -154,7 +157,7 @@
 
   // Card number (PAN).
   AutofillEditItem* cardNumberitem =
-      [[[AutofillEditItem alloc] initWithType:ItemTypeCardNumber] autorelease];
+      [[AutofillEditItem alloc] initWithType:ItemTypeCardNumber];
   cardNumberitem.textFieldName =
       l10n_util::GetNSString(IDS_IOS_AUTOFILL_CARD_NUMBER);
   // Never show full card number for Wallet cards, even if copied locally.
@@ -170,8 +173,8 @@
       toSectionWithIdentifier:SectionIdentifierFields];
 
   // Expiration month.
-  AutofillEditItem* expirationMonthItem = [[[AutofillEditItem alloc]
-      initWithType:ItemTypeExpirationMonth] autorelease];
+  AutofillEditItem* expirationMonthItem =
+      [[AutofillEditItem alloc] initWithType:ItemTypeExpirationMonth];
   expirationMonthItem.textFieldName =
       l10n_util::GetNSString(IDS_IOS_AUTOFILL_EXP_MONTH);
   expirationMonthItem.textFieldValue =
@@ -182,8 +185,8 @@
       toSectionWithIdentifier:SectionIdentifierFields];
 
   // Expiration year.
-  AutofillEditItem* expirationYearItem = [[[AutofillEditItem alloc]
-      initWithType:ItemTypeExpirationYear] autorelease];
+  AutofillEditItem* expirationYearItem =
+      [[AutofillEditItem alloc] initWithType:ItemTypeExpirationYear];
   expirationYearItem.textFieldName =
       l10n_util::GetNSString(IDS_IOS_AUTOFILL_EXP_YEAR);
   expirationYearItem.textFieldValue =
@@ -196,8 +199,8 @@
   if (_creditCard.record_type() == autofill::CreditCard::FULL_SERVER_CARD) {
     // Add CopiedToChrome cell in its own section.
     [model addSectionWithIdentifier:SectionIdentifierCopiedToChrome];
-    CopiedToChromeItem* copiedToChromeItem = [[[CopiedToChromeItem alloc]
-        initWithType:ItemTypeCopiedToChrome] autorelease];
+    CopiedToChromeItem* copiedToChromeItem =
+        [[CopiedToChromeItem alloc] initWithType:ItemTypeCopiedToChrome];
     [model addItem:copiedToChromeItem
         toSectionWithIdentifier:SectionIdentifierCopiedToChrome];
   }
diff --git a/ios/chrome/browser/ui/settings/autofill_edit_collection_view_controller.mm b/ios/chrome/browser/ui/settings/autofill_edit_collection_view_controller.mm
index 660cde5..00458527 100644
--- a/ios/chrome/browser/ui/settings/autofill_edit_collection_view_controller.mm
+++ b/ios/chrome/browser/ui/settings/autofill_edit_collection_view_controller.mm
@@ -6,12 +6,15 @@
 
 #include "base/logging.h"
 #import "base/mac/foundation_util.h"
-#import "base/mac/scoped_nsobject.h"
 #import "ios/chrome/browser/ui/autofill/autofill_edit_accessory_view.h"
 #import "ios/chrome/browser/ui/autofill/cells/autofill_edit_item.h"
 #import "ios/chrome/browser/ui/settings/autofill_edit_collection_view_controller+protected.h"
 #import "ios/third_party/material_components_ios/src/components/CollectionCells/src/MaterialCollectionCells.h"
 
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
 namespace {
 
 AutofillEditCell* AutofillEditCellForTextField(UITextField* textField) {
@@ -33,8 +36,8 @@
 
 @interface AutofillEditCollectionViewController ()<
     AutofillEditAccessoryDelegate> {
-  base::scoped_nsobject<AutofillEditCell> _currentEditingCell;
-  base::scoped_nsobject<AutofillEditAccessoryView> _accessoryView;
+  AutofillEditCell* _currentEditingCell;
+  AutofillEditAccessoryView* _accessoryView;
 }
 @end
 
@@ -46,8 +49,7 @@
     return nil;
   }
 
-  _accessoryView.reset(
-      [[AutofillEditAccessoryView alloc] initWithDelegate:self]);
+  _accessoryView = [[AutofillEditAccessoryView alloc] initWithDelegate:self];
   [self setShouldHideDoneButton:YES];
   [self updateEditButton];
   return self;
@@ -72,7 +74,6 @@
 
 - (void)dealloc {
   [[NSNotificationCenter defaultCenter] removeObserver:self];
-  [super dealloc];
 }
 
 #pragma mark - SettingsRootCollectionViewController
@@ -109,7 +110,7 @@
 
 - (void)textFieldDidBeginEditing:(UITextField*)textField {
   AutofillEditCell* cell = AutofillEditCellForTextField(textField);
-  _currentEditingCell.reset([cell retain]);
+  _currentEditingCell = cell;
   [textField setInputAccessoryView:_accessoryView];
   [self updateAccessoryViewButtonState];
 }
@@ -118,7 +119,7 @@
   AutofillEditCell* cell = AutofillEditCellForTextField(textField);
   DCHECK(_currentEditingCell == cell);
   [textField setInputAccessoryView:nil];
-  _currentEditingCell.reset(nil);
+  _currentEditingCell = nil;
 }
 
 - (BOOL)textFieldShouldReturn:(UITextField*)textField {
diff --git a/ios/chrome/browser/ui/settings/autofill_profile_edit_collection_view_controller.mm b/ios/chrome/browser/ui/settings/autofill_profile_edit_collection_view_controller.mm
index dc60aff..6f9cc2b 100644
--- a/ios/chrome/browser/ui/settings/autofill_profile_edit_collection_view_controller.mm
+++ b/ios/chrome/browser/ui/settings/autofill_profile_edit_collection_view_controller.mm
@@ -4,9 +4,7 @@
 
 #import "ios/chrome/browser/ui/settings/autofill_profile_edit_collection_view_controller.h"
 
-#import "base/ios/weak_nsobject.h"
 #include "base/mac/foundation_util.h"
-#import "base/mac/scoped_nsobject.h"
 #include "base/strings/sys_string_conversions.h"
 #include "components/autofill/core/browser/autofill_profile.h"
 #include "components/autofill/core/browser/field_types.h"
@@ -24,6 +22,10 @@
 #include "ui/base/l10n/l10n_util.h"
 #include "url/gurl.h"
 
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
 NSString* const kAutofillProfileEditCollectionViewId =
     @"kAutofillProfileEditCollectionViewId";
 
@@ -109,8 +111,7 @@
 + (instancetype)controllerWithProfile:(const autofill::AutofillProfile&)profile
                   personalDataManager:
                       (autofill::PersonalDataManager*)dataManager {
-  return [[[self alloc] initWithProfile:profile personalDataManager:dataManager]
-      autorelease];
+  return [[self alloc] initWithProfile:profile personalDataManager:dataManager];
 }
 
 #pragma mark - SettingsRootCollectionViewController
@@ -120,8 +121,8 @@
   if (_autofillProfile.record_type() ==
       autofill::AutofillProfile::SERVER_PROFILE) {
     GURL paymentsURL = autofill::payments::GetManageAddressesUrl(0);
-    base::scoped_nsobject<OpenUrlCommand> command(
-        [[OpenUrlCommand alloc] initWithURLFromChrome:paymentsURL]);
+    OpenUrlCommand* command =
+        [[OpenUrlCommand alloc] initWithURLFromChrome:paymentsURL];
     [command setTag:IDC_CLOSE_SETTINGS_AND_OPEN_URL];
     [self chromeExecuteCommand:command];
 
@@ -181,7 +182,7 @@
   for (size_t i = 0; i < arraysize(kFieldsToDisplay); ++i) {
     const AutofillFieldDisplayInfo& field = kFieldsToDisplay[i];
     AutofillEditItem* item =
-        [[[AutofillEditItem alloc] initWithType:ItemTypeField] autorelease];
+        [[AutofillEditItem alloc] initWithType:ItemTypeField];
     item.textFieldName = l10n_util::GetNSString(field.displayStringID);
     item.textFieldValue = base::SysUTF16ToNSString(_autofillProfile.GetInfo(
         autofill::AutofillType(field.autofillType), locale));
diff --git a/ios/chrome/browser/ui/settings/bandwidth_management_collection_view_controller.mm b/ios/chrome/browser/ui/settings/bandwidth_management_collection_view_controller.mm
index 18eefc1..26e8487c 100644
--- a/ios/chrome/browser/ui/settings/bandwidth_management_collection_view_controller.mm
+++ b/ios/chrome/browser/ui/settings/bandwidth_management_collection_view_controller.mm
@@ -5,7 +5,6 @@
 #import "ios/chrome/browser/ui/settings/bandwidth_management_collection_view_controller.h"
 
 #include "base/mac/foundation_util.h"
-#import "base/mac/scoped_nsobject.h"
 #include "components/prefs/pref_change_registrar.h"
 #include "components/prefs/pref_service.h"
 #include "ios/chrome/browser/browser_state/chrome_browser_state.h"
@@ -25,6 +24,10 @@
 #include "ui/base/l10n/l10n_util_mac.h"
 #include "url/gurl.h"
 
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
 namespace {
 
 typedef NS_ENUM(NSInteger, SectionIdentifier) {
@@ -49,7 +52,7 @@
   PrefChangeRegistrar _prefChangeRegistrarApplicationContext;
 
   // Updatable Items
-  base::scoped_nsobject<CollectionViewDetailItem> _preloadWebpagesDetailItem;
+  CollectionViewDetailItem* _preloadWebpagesDetailItem;
 }
 
 // Helpers to create collection view items.
@@ -102,22 +105,21 @@
       currentLabelForPreference:_browserState->GetPrefs()
                        basePref:prefs::kNetworkPredictionEnabled
                        wifiPref:prefs::kNetworkPredictionWifiOnly];
-  _preloadWebpagesDetailItem.reset(
-      [[CollectionViewDetailItem alloc] initWithType:ItemTypePreload]);
+  _preloadWebpagesDetailItem =
+      [[CollectionViewDetailItem alloc] initWithType:ItemTypePreload];
 
-  _preloadWebpagesDetailItem.get().text =
+  _preloadWebpagesDetailItem.text =
       l10n_util::GetNSString(IDS_IOS_OPTIONS_PRELOAD_WEBPAGES);
-  _preloadWebpagesDetailItem.get().detailText = detailText;
-  _preloadWebpagesDetailItem.get().accessoryType =
+  _preloadWebpagesDetailItem.detailText = detailText;
+  _preloadWebpagesDetailItem.accessoryType =
       MDCCollectionViewCellAccessoryDisclosureIndicator;
-  _preloadWebpagesDetailItem.get().accessibilityTraits |=
-      UIAccessibilityTraitButton;
+  _preloadWebpagesDetailItem.accessibilityTraits |= UIAccessibilityTraitButton;
   return _preloadWebpagesDetailItem;
 }
 
 - (CollectionViewItem*)footerItem {
-  CollectionViewFooterItem* item = [[[CollectionViewFooterItem alloc]
-      initWithType:ItemTypeFooter] autorelease];
+  CollectionViewFooterItem* item =
+      [[CollectionViewFooterItem alloc] initWithType:ItemTypeFooter];
 
   item.text = l10n_util::GetNSString(
       IDS_IOS_BANDWIDTH_MANAGEMENT_DESCRIPTION_LEARN_MORE);
@@ -148,12 +150,12 @@
   if (type == ItemTypePreload) {
     NSString* preloadTitle =
         l10n_util::GetNSString(IDS_IOS_OPTIONS_PRELOAD_WEBPAGES);
-    base::scoped_nsobject<UIViewController> controller(
+    UIViewController* controller =
         [[DataplanUsageCollectionViewController alloc]
             initWithPrefs:_browserState->GetPrefs()
                  basePref:prefs::kNetworkPredictionEnabled
                  wifiPref:prefs::kNetworkPredictionWifiOnly
-                    title:preloadTitle]);
+                    title:preloadTitle];
     [self.navigationController pushViewController:controller animated:YES];
   }
 }
@@ -210,7 +212,7 @@
                          basePref:prefs::kNetworkPredictionEnabled
                          wifiPref:prefs::kNetworkPredictionWifiOnly];
 
-    _preloadWebpagesDetailItem.get().detailText = detailText;
+    _preloadWebpagesDetailItem.detailText = detailText;
 
     [self reconfigureCellsForItems:@[ _preloadWebpagesDetailItem ]
            inSectionWithIdentifier:SectionIdentifierActions];
diff --git a/ios/chrome/browser/ui/settings/bar_button_activity_indicator.mm b/ios/chrome/browser/ui/settings/bar_button_activity_indicator.mm
index 5586ac5..0f4734d7 100644
--- a/ios/chrome/browser/ui/settings/bar_button_activity_indicator.mm
+++ b/ios/chrome/browser/ui/settings/bar_button_activity_indicator.mm
@@ -4,18 +4,21 @@
 
 #import "ios/chrome/browser/ui/settings/bar_button_activity_indicator.h"
 
-#import "base/mac/scoped_nsobject.h"
 #include "ios/chrome/browser/ui/ui_util.h"
 
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
 @implementation BarButtonActivityIndicator {
-  base::scoped_nsobject<UIActivityIndicatorView> activityIndicator_;
+  UIActivityIndicatorView* activityIndicator_;
 }
 
 - (id)initWithFrame:(CGRect)frame {
   self = [super initWithFrame:frame];
   if (self) {
-    activityIndicator_.reset([[UIActivityIndicatorView alloc]
-        initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray]);
+    activityIndicator_ = [[UIActivityIndicatorView alloc]
+        initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray];
     [activityIndicator_ setBackgroundColor:[UIColor clearColor]];
     [activityIndicator_ setHidesWhenStopped:YES];
     [activityIndicator_ startAnimating];
@@ -26,7 +29,6 @@
 
 - (void)dealloc {
   [activityIndicator_ stopAnimating];
-  [super dealloc];
 }
 
 - (void)layoutSubviews {
diff --git a/ios/chrome/browser/ui/settings/block_popups_collection_view_controller.mm b/ios/chrome/browser/ui/settings/block_popups_collection_view_controller.mm
index db548f6..c562d60 100644
--- a/ios/chrome/browser/ui/settings/block_popups_collection_view_controller.mm
+++ b/ios/chrome/browser/ui/settings/block_popups_collection_view_controller.mm
@@ -4,10 +4,8 @@
 
 #import "ios/chrome/browser/ui/settings/block_popups_collection_view_controller.h"
 
-#import "base/ios/weak_nsobject.h"
 #include "base/logging.h"
 #import "base/mac/foundation_util.h"
-#import "base/mac/scoped_nsobject.h"
 #include "base/strings/sys_string_conversions.h"
 #include "base/values.h"
 #include "components/content_settings/core/browser/host_content_settings_map.h"
@@ -24,6 +22,10 @@
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/base/l10n/l10n_util_mac.h"
 
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
 namespace {
 
 typedef NS_ENUM(NSInteger, SectionIdentifier) {
@@ -46,10 +48,10 @@
   base::ListValue _exceptions;
 
   // The observable boolean that binds to the "Disable Popups" setting state.
-  base::scoped_nsobject<ContentSettingBackedBoolean> _disablePopupsSetting;
+  ContentSettingBackedBoolean* _disablePopupsSetting;
 
   // The item related to the switch for the "Disable Popups" setting.
-  base::scoped_nsobject<CollectionViewSwitchItem> _blockPopupsItem;
+  CollectionViewSwitchItem* _blockPopupsItem;
 }
 
 // Fetch the urls that can display popups and add them to |_exceptions|.
@@ -69,10 +71,10 @@
     _browserState = browserState;
     HostContentSettingsMap* settingsMap =
         ios::HostContentSettingsMapFactory::GetForBrowserState(_browserState);
-    _disablePopupsSetting.reset([[ContentSettingBackedBoolean alloc]
+    _disablePopupsSetting = [[ContentSettingBackedBoolean alloc]
         initWithHostContentSettingsMap:settingsMap
                              settingID:CONTENT_SETTINGS_TYPE_POPUPS
-                              inverted:YES]);
+                              inverted:YES];
     [_disablePopupsSetting setObserver:self];
     self.title = l10n_util::GetNSString(IDS_IOS_BLOCK_POPUPS);
     self.collectionViewAccessibilityIdentifier =
@@ -87,7 +89,6 @@
 
 - (void)dealloc {
   [_disablePopupsSetting setObserver:nil];
-  [super dealloc];
 }
 
 #pragma mark - SettingsRootCollectionViewController
@@ -100,12 +101,11 @@
   // Block popups switch.
   [model addSectionWithIdentifier:SectionIdentifierMainSwitch];
 
-  _blockPopupsItem.reset(
-      [[CollectionViewSwitchItem alloc] initWithType:ItemTypeMainSwitch]);
-  _blockPopupsItem.get().text = l10n_util::GetNSString(IDS_IOS_BLOCK_POPUPS);
-  _blockPopupsItem.get().on = [_disablePopupsSetting value];
-  _blockPopupsItem.get().accessibilityIdentifier =
-      @"blockPopupsContentView_switch";
+  _blockPopupsItem =
+      [[CollectionViewSwitchItem alloc] initWithType:ItemTypeMainSwitch];
+  _blockPopupsItem.text = l10n_util::GetNSString(IDS_IOS_BLOCK_POPUPS);
+  _blockPopupsItem.on = [_disablePopupsSetting value];
+  _blockPopupsItem.accessibilityIdentifier = @"blockPopupsContentView_switch";
   [model addItem:_blockPopupsItem
       toSectionWithIdentifier:SectionIdentifierMainSwitch];
 
@@ -187,18 +187,17 @@
     NSInteger exceptionsSectionIndex = [self.collectionViewModel
         sectionForSectionIdentifier:SectionIdentifierExceptions];
     if ([collectionView numberOfItemsInSection:exceptionsSectionIndex] == 0) {
-      base::WeakNSObject<BlockPopupsCollectionViewController> weakSelf(self);
+      __weak BlockPopupsCollectionViewController* weakSelf = self;
       [self.collectionView performBatchUpdates:^{
-        base::scoped_nsobject<BlockPopupsCollectionViewController> strongSelf(
-            [weakSelf retain]);
+        BlockPopupsCollectionViewController* strongSelf = weakSelf;
         if (!strongSelf) {
           return;
         }
-        NSInteger section = [strongSelf.get().collectionViewModel
+        NSInteger section = [strongSelf.collectionViewModel
             sectionForSectionIdentifier:SectionIdentifierExceptions];
-        [strongSelf.get().collectionViewModel
+        [strongSelf.collectionViewModel
             removeSectionWithIdentifier:SectionIdentifierExceptions];
-        [strongSelf.get().collectionView
+        [strongSelf.collectionView
             deleteSections:[NSIndexSet indexSetWithIndex:section]];
       }
                                     completion:nil];
@@ -222,10 +221,10 @@
 #pragma mark - BooleanObserver
 
 - (void)booleanDidChange:(id<ObservableBoolean>)observableBoolean {
-  DCHECK_EQ(observableBoolean, _disablePopupsSetting.get());
+  DCHECK_EQ(observableBoolean, _disablePopupsSetting);
 
   // Update the item.
-  _blockPopupsItem.get().on = [_disablePopupsSetting value];
+  _blockPopupsItem.on = [_disablePopupsSetting value];
 
   // Update the cell.
   [self reconfigureCellsForItems:@[ _blockPopupsItem ]
@@ -244,7 +243,7 @@
   [_disablePopupsSetting setValue:switchView.on];
 
   // Update the item.
-  _blockPopupsItem.get().on = [_disablePopupsSetting value];
+  _blockPopupsItem.on = [_disablePopupsSetting value];
 
   // Update the rest of the UI.
   [self.editor setEditing:NO];
@@ -294,8 +293,8 @@
   CollectionViewModel* model = self.collectionViewModel;
   [model addSectionWithIdentifier:SectionIdentifierExceptions];
 
-  CollectionViewTextItem* header = [
-      [[CollectionViewTextItem alloc] initWithType:ItemTypeHeader] autorelease];
+  CollectionViewTextItem* header =
+      [[CollectionViewTextItem alloc] initWithType:ItemTypeHeader];
   header.text = l10n_util::GetNSString(IDS_IOS_POPUPS_ALLOWED);
   header.textColor = [[MDCPalette greyPalette] tint500];
   [model setHeader:header forSectionWithIdentifier:SectionIdentifierExceptions];
@@ -303,8 +302,8 @@
   for (size_t i = 0; i < _exceptions.GetSize(); ++i) {
     std::string allowed_url;
     _exceptions.GetString(i, &allowed_url);
-    CollectionViewTextItem* item = [[[CollectionViewTextItem alloc]
-        initWithType:ItemTypeException] autorelease];
+    CollectionViewTextItem* item =
+        [[CollectionViewTextItem alloc] initWithType:ItemTypeException];
     item.text = base::SysUTF8ToNSString(allowed_url);
     [model addItem:item toSectionWithIdentifier:SectionIdentifierExceptions];
   }
@@ -318,10 +317,9 @@
   if (blockPopupsIsOn && !exceptionsListShown && hasExceptions) {
     // Animate in the list of exceptions. Animation looks much better if the
     // section is added at once, rather than row-by-row as each object is added.
-    base::WeakNSObject<BlockPopupsCollectionViewController> weakSelf(self);
+    __weak BlockPopupsCollectionViewController* weakSelf = self;
     [self.collectionView performBatchUpdates:^{
-      base::scoped_nsobject<BlockPopupsCollectionViewController> strongSelf(
-          [weakSelf retain]);
+      BlockPopupsCollectionViewController* strongSelf = weakSelf;
       if (!strongSelf)
         return;
       [strongSelf populateExceptionsItems];
@@ -333,10 +331,9 @@
                                   completion:nil];
   } else if (!blockPopupsIsOn && exceptionsListShown) {
     // Make sure the exception section is not shown.
-    base::WeakNSObject<BlockPopupsCollectionViewController> weakSelf(self);
+    __weak BlockPopupsCollectionViewController* weakSelf = self;
     [self.collectionView performBatchUpdates:^{
-      base::scoped_nsobject<BlockPopupsCollectionViewController> strongSelf(
-          [weakSelf retain]);
+      BlockPopupsCollectionViewController* strongSelf = weakSelf;
       if (!strongSelf)
         return;
       NSUInteger index = [[strongSelf collectionViewModel]
diff --git a/ios/chrome/browser/ui/settings/clear_browsing_data_collection_view_controller.mm b/ios/chrome/browser/ui/settings/clear_browsing_data_collection_view_controller.mm
index 5e65e4f..1a128bd 100644
--- a/ios/chrome/browser/ui/settings/clear_browsing_data_collection_view_controller.mm
+++ b/ios/chrome/browser/ui/settings/clear_browsing_data_collection_view_controller.mm
@@ -8,11 +8,9 @@
 #include <string>
 
 #include "base/ios/ios_util.h"
-#import "base/ios/weak_nsobject.h"
 #include "base/logging.h"
 #import "base/mac/bind_objc_block.h"
 #include "base/mac/foundation_util.h"
-#import "base/mac/scoped_nsobject.h"
 #include "base/memory/ptr_util.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/strings/string_piece.h"
@@ -61,6 +59,10 @@
 #include "ui/base/l10n/l10n_util_mac.h"
 #include "url/gurl.h"
 
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
 NSString* const kClearBrowsingDataCollectionViewId =
     @"kClearBrowsingDataCollectionViewId";
 NSString* const kClearBrowsingHistoryCellId = @"kClearBrowsingHistoryCellId";
@@ -207,18 +209,16 @@
         kClearBrowsingDataCollectionViewId;
 
     if (experimental_flags::IsNewClearBrowsingDataUIEnabled()) {
-      base::WeakNSObject<ClearBrowsingDataCollectionViewController> weakSelf(
-          self);
+      __weak ClearBrowsingDataCollectionViewController* weakSelf = self;
       void (^dataClearedCallback)(
           const IOSChromeBrowsingDataRemover::NotificationDetails&) =
           ^(const IOSChromeBrowsingDataRemover::NotificationDetails& details) {
-            base::scoped_nsobject<ClearBrowsingDataCollectionViewController>
-                strongSelf([weakSelf retain]);
+            ClearBrowsingDataCollectionViewController* strongSelf = weakSelf;
             [strongSelf restartCounters:details.removal_mask];
           };
       _callbackSubscription =
           IOSChromeBrowsingDataRemover::RegisterOnBrowsingDataRemovedCallback(
-              base::BindBlock(dataClearedCallback));
+              base::BindBlockArc(dataClearedCallback));
     }
 
     [self loadModel];
@@ -241,20 +241,18 @@
   history::WebHistoryService* historyService =
       ios::WebHistoryServiceFactory::GetForBrowserState(_browserState);
 
-  base::WeakNSObject<ClearBrowsingDataCollectionViewController> weakSelf(self);
+  __weak ClearBrowsingDataCollectionViewController* weakSelf = self;
   browsing_data::ShouldShowNoticeAboutOtherFormsOfBrowsingHistory(
-      syncService, historyService, base::BindBlock(^(bool shouldShowNotice) {
-        base::scoped_nsobject<ClearBrowsingDataCollectionViewController>
-            strongSelf([weakSelf retain]);
+      syncService, historyService, base::BindBlockArc(^(bool shouldShowNotice) {
+        ClearBrowsingDataCollectionViewController* strongSelf = weakSelf;
         [strongSelf setShouldShowNoticeAboutOtherFormsOfBrowsingHistory:
                         shouldShowNotice];
       }));
 
   browsing_data::ShouldPopupDialogAboutOtherFormsOfBrowsingHistory(
       syncService, historyService, GetChannel(),
-      base::BindBlock(^(bool shouldShowPopup) {
-        base::scoped_nsobject<ClearBrowsingDataCollectionViewController>
-            strongSelf([weakSelf retain]);
+      base::BindBlockArc(^(bool shouldShowPopup) {
+        ClearBrowsingDataCollectionViewController* strongSelf = weakSelf;
         [strongSelf setShouldPopupDialogAboutOtherFormsOfBrowsingHistory:
                         shouldShowPopup];
       }));
@@ -328,8 +326,8 @@
 
   // Clear Browsing Data button.
   [model addSectionWithIdentifier:SectionIdentifierClearBrowsingDataButton];
-  CollectionViewTextItem* clearButtonItem = [[[CollectionViewTextItem alloc]
-      initWithType:ItemTypeClearBrowsingDataButton] autorelease];
+  CollectionViewTextItem* clearButtonItem = [[CollectionViewTextItem alloc]
+      initWithType:ItemTypeClearBrowsingDataButton];
   clearButtonItem.text = l10n_util::GetNSString(IDS_IOS_CLEAR_BUTTON);
   clearButtonItem.accessibilityTraits |= UIAccessibilityTraitButton;
   clearButtonItem.textColor = [[MDCPalette cr_redPalette] tint500];
@@ -371,8 +369,7 @@
                                    mask:(int)mask
                                prefName:(const char*)prefName {
   PrefService* prefs = _browserState->GetPrefs();
-  ClearDataItem* clearDataItem =
-      [[[ClearDataItem alloc] initWithType:itemType] autorelease];
+  ClearDataItem* clearDataItem = [[ClearDataItem alloc] initWithType:itemType];
   clearDataItem.text = l10n_util::GetNSString(titleMessageID);
   if (prefs->GetBoolean(prefName)) {
     clearDataItem.accessoryType = MDCCollectionViewCellAccessoryCheckmark;
@@ -382,18 +379,17 @@
   clearDataItem.accessibilityIdentifier =
       [self getAccessibilityIdentifierFromItemType:itemType];
 
-  base::WeakNSObject<ClearBrowsingDataCollectionViewController> weakSelf(self);
+  __weak ClearBrowsingDataCollectionViewController* weakSelf = self;
   void (^updateUICallback)(const browsing_data::BrowsingDataCounter::Result&) =
       ^(const browsing_data::BrowsingDataCounter::Result& result) {
-        base::scoped_nsobject<ClearBrowsingDataCollectionViewController>
-            strongSelf([weakSelf retain]);
+        ClearBrowsingDataCollectionViewController* strongSelf = weakSelf;
         NSString* counterText = [strongSelf getCounterTextFromResult:result];
         [strongSelf updateCounter:itemType detailText:counterText];
       };
 
   [clearDataItem setCounter:BrowsingDataCounterWrapper::CreateCounterWrapper(
                                 prefName, _browserState, prefs,
-                                base::BindBlock(updateUICallback))];
+                                base::BindBlockArc(updateUICallback))];
   return clearDataItem;
 }
 
@@ -426,8 +422,8 @@
 }
 
 - (CollectionViewItem*)footerGoogleAccountItem {
-  CollectionViewFooterItem* footerItem = [[[CollectionViewFooterItem alloc]
-      initWithType:ItemTypeFooterGoogleAccount] autorelease];
+  CollectionViewFooterItem* footerItem = [[CollectionViewFooterItem alloc]
+      initWithType:ItemTypeFooterGoogleAccount];
   footerItem.text =
       l10n_util::GetNSString(IDS_IOS_CLEAR_BROWSING_DATA_FOOTER_ACCOUNT);
   UIImage* image = ios::GetChromeBrowserProvider()
@@ -475,7 +471,7 @@
                                       URL:(const char[])URL
                                     image:(UIImage*)image {
   CollectionViewFooterItem* footerItem =
-      [[[CollectionViewFooterItem alloc] initWithType:itemType] autorelease];
+      [[CollectionViewFooterItem alloc] initWithType:itemType];
   footerItem.text = l10n_util::GetNSString(titleMessageID);
   footerItem.linkURL = google_util::AppendGoogleLocaleParam(
       GURL(URL), GetApplicationContext()->GetApplicationLocale());
@@ -485,8 +481,8 @@
 }
 
 - (CollectionViewItem*)timeRangeItem {
-  CollectionViewDetailItem* timeRangeItem = [[[CollectionViewDetailItem alloc]
-      initWithType:ItemTypeTimeRange] autorelease];
+  CollectionViewDetailItem* timeRangeItem =
+      [[CollectionViewDetailItem alloc] initWithType:ItemTypeTimeRange];
   timeRangeItem.text = l10n_util::GetNSString(
       IDS_IOS_CLEAR_BROWSING_DATA_TIME_RANGE_SELECTOR_TITLE);
   NSString* detailText = [TimeRangeSelectorCollectionViewController
@@ -509,10 +505,10 @@
 
   switch (itemType) {
     case ItemTypeTimeRange: {
-      base::scoped_nsobject<UIViewController> controller(
+      UIViewController* controller =
           [[TimeRangeSelectorCollectionViewController alloc]
               initWithPrefs:_browserState->GetPrefs()
-                   delegate:self]);
+                   delegate:self];
       [self.navigationController pushViewController:controller animated:YES];
       break;
     }
@@ -612,7 +608,7 @@
     // Nothing to clear (no data types selected).
     return YES;
   }
-  base::WeakNSObject<ClearBrowsingDataCollectionViewController> weakSelf(self);
+  __weak ClearBrowsingDataCollectionViewController* weakSelf = self;
   UIAlertController* alertController = [UIAlertController
       alertControllerWithTitle:nil
                        message:nil
@@ -638,10 +634,10 @@
 
 - (void)clearDataForDataTypes:(int)dataTypeMask {
   DCHECK(dataTypeMask);
-  base::scoped_nsobject<ClearBrowsingDataCommand> command(
+  ClearBrowsingDataCommand* command =
       [[ClearBrowsingDataCommand alloc] initWithBrowserState:_browserState
                                                         mask:dataTypeMask
-                                                  timePeriod:_timePeriod]);
+                                                  timePeriod:_timePeriod];
   [self chromeExecuteCommand:command];
 
   if (!!(dataTypeMask && IOSChromeBrowsingDataRemover::REMOVE_HISTORY)) {
@@ -682,7 +678,7 @@
                                           message:message
                                    preferredStyle:UIAlertControllerStyleAlert];
 
-  base::WeakNSObject<ClearBrowsingDataCollectionViewController> weakSelf(self);
+  __weak ClearBrowsingDataCollectionViewController* weakSelf = self;
   UIAlertAction* openMyActivityAction = [UIAlertAction
       actionWithTitle:
           l10n_util::GetNSString(
@@ -703,9 +699,9 @@
 }
 
 - (void)openMyActivityLink {
-  base::scoped_nsobject<OpenUrlCommand> openMyActivityCommand(
-      [[OpenUrlCommand alloc] initWithURLFromChrome:GURL(kGoogleMyAccountURL)]);
-  openMyActivityCommand.get().tag = IDC_CLOSE_SETTINGS_AND_OPEN_URL;
+  OpenUrlCommand* openMyActivityCommand =
+      [[OpenUrlCommand alloc] initWithURLFromChrome:GURL(kGoogleMyAccountURL)];
+  openMyActivityCommand.tag = IDC_CLOSE_SETTINGS_AND_OPEN_URL;
   [self chromeExecuteCommand:openMyActivityCommand];
 }
 
@@ -784,12 +780,11 @@
     // Because of this, the lowest unit that can be used is MB.
     static const int kBytesInAMegabyte = 1 << 20;
     if (cacheSizeBytes >= kBytesInAMegabyte) {
-      base::scoped_nsobject<NSByteCountFormatter> formatter(
-          [[NSByteCountFormatter alloc] init]);
-      formatter.get().allowedUnits = NSByteCountFormatterUseAll &
-                                     (~NSByteCountFormatterUseBytes) &
-                                     (~NSByteCountFormatterUseKB);
-      formatter.get().countStyle = NSByteCountFormatterCountStyleMemory;
+      NSByteCountFormatter* formatter = [[NSByteCountFormatter alloc] init];
+      formatter.allowedUnits = NSByteCountFormatterUseAll &
+                               (~NSByteCountFormatterUseBytes) &
+                               (~NSByteCountFormatterUseKB);
+      formatter.countStyle = NSByteCountFormatterCountStyleMemory;
       NSString* formattedSize = [formatter stringFromByteCount:cacheSizeBytes];
       return (_timePeriod == browsing_data::TimePeriod::ALL_TIME)
                  ? formattedSize
diff --git a/ios/clean/chrome/app/app_delegate.mm b/ios/clean/chrome/app/app_delegate.mm
index 5a3293b..fe81355 100644
--- a/ios/clean/chrome/app/app_delegate.mm
+++ b/ios/clean/chrome/app/app_delegate.mm
@@ -8,7 +8,7 @@
 #import "ios/clean/chrome/app/steps/launch_to_background.h"
 #import "ios/clean/chrome/app/steps/launch_to_basic.h"
 #import "ios/clean/chrome/app/steps/launch_to_foreground.h"
-#import "ios/clean/chrome/app/steps/tab_grid_coordinator+application_step.h"
+#import "ios/clean/chrome/app/steps/root_coordinator+application_step.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
 #error "This file requires ARC support."
@@ -109,7 +109,7 @@
     [[BeginForegrounding alloc] init],
     [[PrepareForUI alloc] init],
     [[CompleteForegrounding alloc] init],
-    [[TabGridCoordinator alloc] init],
+    [[RootCoordinator alloc] init],
   ]];
 }
 
diff --git a/ios/clean/chrome/app/steps/BUILD.gn b/ios/clean/chrome/app/steps/BUILD.gn
index 7da46f0..e87ff249 100644
--- a/ios/clean/chrome/app/steps/BUILD.gn
+++ b/ios/clean/chrome/app/steps/BUILD.gn
@@ -10,8 +10,8 @@
     "launch_to_basic.mm",
     "launch_to_foreground.h",
     "launch_to_foreground.mm",
-    "tab_grid_coordinator+application_step.h",
-    "tab_grid_coordinator+application_step.mm",
+    "root_coordinator+application_step.h",
+    "root_coordinator+application_step.mm",
   ]
 
   configs += [ "//build/config/compiler:enable_arc" ]
@@ -28,10 +28,28 @@
     "//ios/chrome/browser/content_settings",
     "//ios/chrome/browser/web:web_internal",
     "//ios/clean/chrome/app:application_state",
-    "//ios/clean/chrome/browser/ui/tab_grid",
+    "//ios/clean/chrome/browser/ui/root",
     "//ios/net",
     "//ios/shared/chrome/browser/ui/browser_list",
     "//ios/shared/chrome/browser/ui/coordinators",
     "//ios/web:web_arc",
   ]
 }
+
+source_set("unit_tests") {
+  testonly = true
+
+  sources = [
+    "root_coordinator+application_step_unittest.mm",
+  ]
+
+  configs += [ "//build/config/compiler:enable_arc" ]
+
+  deps = [
+    ":steps",
+    "//base",
+    "//ios/clean/chrome/app:application_state",
+    "//testing/gtest",
+    "//third_party/ocmock",
+  ]
+}
diff --git a/ios/clean/chrome/app/steps/root_coordinator+application_step.h b/ios/clean/chrome/app/steps/root_coordinator+application_step.h
new file mode 100644
index 0000000..4558831
--- /dev/null
+++ b/ios/clean/chrome/app/steps/root_coordinator+application_step.h
@@ -0,0 +1,21 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef IOS_CLEAN_CHROME_APP_STEPS_ROOT_COORDINATOR_APPLICATION_STEP_H_
+#define IOS_CLEAN_CHROME_APP_STEPS_ROOT_COORDINATOR_APPLICATION_STEP_H_
+
+#import "ios/clean/chrome/app/application_step.h"
+#import "ios/clean/chrome/browser/ui/root/root_coordinator.h"
+
+// Category on RootCoordinator to allow it to act as an application
+// step and control the root UI for the application when is starts.
+// Creates the main window and makes it key, but doesn't make it visible yet.
+//  Pre:  Application phase is APPLICATION_FOREGROUNDED and the main window is
+//        key.
+//  Post: Application phase is (still) APPLICATION_FOREGROUNDED, the main window
+//        is visible and has a root view controller set.
+@interface RootCoordinator (ApplicationStep)<ApplicationStep>
+@end
+
+#endif  // IOS_CLEAN_CHROME_APP_STEPS_ROOT_COORDINATOR_APPLICATION_STEP_H_
diff --git a/ios/clean/chrome/app/steps/tab_grid_coordinator+application_step.mm b/ios/clean/chrome/app/steps/root_coordinator+application_step.mm
similarity index 85%
rename from ios/clean/chrome/app/steps/tab_grid_coordinator+application_step.mm
rename to ios/clean/chrome/app/steps/root_coordinator+application_step.mm
index 5e6ebbe..73da4e99 100644
--- a/ios/clean/chrome/app/steps/tab_grid_coordinator+application_step.mm
+++ b/ios/clean/chrome/app/steps/root_coordinator+application_step.mm
@@ -1,8 +1,8 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
+// Copyright 2017 The Chromium Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#import "ios/clean/chrome/app/steps/tab_grid_coordinator+application_step.h"
+#import "ios/clean/chrome/app/steps/root_coordinator+application_step.h"
 
 #import "base/supports_user_data.h"
 #import "ios/clean/chrome/app/application_state.h"
@@ -31,10 +31,10 @@
 const char kRootCoordinatorContainerKey[] = "root_coordinator";
 }  // namespace
 
-@interface StopTabGridCoordinator : NSObject<ApplicationStep>
+@interface StopRootCoordinator : NSObject<ApplicationStep>
 @end
 
-@implementation TabGridCoordinator (ApplicationStep)
+@implementation RootCoordinator (ApplicationStep)
 
 - (BOOL)canRunInState:(ApplicationState*)state {
   return [state.window isKeyWindow] && state.phase == APPLICATION_FOREGROUNDED;
@@ -61,12 +61,12 @@
                                      new RootCoordinatorContainer(self));
 
   // Add a termination step to remove this object.
-  [[state terminationSteps] addObject:[[StopTabGridCoordinator alloc] init]];
+  [[state terminationSteps] addObject:[[StopRootCoordinator alloc] init]];
 }
 
 @end
 
-@implementation StopTabGridCoordinator
+@implementation StopRootCoordinator
 
 - (BOOL)canRunInState:(ApplicationState*)state {
   return state.phase = APPLICATION_TERMINATING;
diff --git a/ios/clean/chrome/app/steps/root_coordinator+application_step_unittest.mm b/ios/clean/chrome/app/steps/root_coordinator+application_step_unittest.mm
new file mode 100644
index 0000000..00a84ca
--- /dev/null
+++ b/ios/clean/chrome/app/steps/root_coordinator+application_step_unittest.mm
@@ -0,0 +1,61 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "ios/clean/chrome/app/steps/root_coordinator+application_step.h"
+
+#include "base/macros.h"
+#import "ios/clean/chrome/app/application_state.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "testing/platform_test.h"
+#import "third_party/ocmock/OCMock/OCMock.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+namespace {
+
+class RootCoordinatorApplicationStepTest : public PlatformTest {
+ public:
+  RootCoordinatorApplicationStepTest() {
+    coordinator_ = [[RootCoordinator alloc] init];
+    state_ = [[ApplicationState alloc] init];
+    window_ = OCMClassMock([UIWindow class]);
+  }
+
+ protected:
+  RootCoordinator* coordinator_;
+  ApplicationState* state_;
+  id window_;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(RootCoordinatorApplicationStepTest);
+};
+
+// Tests that the coordinator can run if the window is key and application is
+// foregrounded.
+TEST_F(RootCoordinatorApplicationStepTest, TestRunsInKeyWindow) {
+  state_.phase = APPLICATION_FOREGROUNDED;
+  state_.window = window_;
+  OCMStub([window_ isKeyWindow]).andReturn(YES);
+  EXPECT_TRUE([coordinator_ canRunInState:state_]);
+}
+
+// Tests that the coordinator cannot run if the window is not key.
+TEST_F(RootCoordinatorApplicationStepTest, TestCannotRunWithoutKeyWindow) {
+  state_.phase = APPLICATION_FOREGROUNDED;
+  state_.window = window_;
+  OCMStub([window_ isKeyWindow]).andReturn(NO);
+  EXPECT_FALSE([coordinator_ canRunInState:state_]);
+}
+
+// Tests that the coordinator cannot run if application is not foregrounded.
+TEST_F(RootCoordinatorApplicationStepTest, TestCannotRunIfNotForegrounded) {
+  state_.phase = APPLICATION_COLD;
+  state_.window = window_;
+  OCMStub([window_ isKeyWindow]).andReturn(YES);
+  EXPECT_FALSE([coordinator_ canRunInState:state_]);
+}
+
+}  // namespace
diff --git a/ios/clean/chrome/app/steps/tab_grid_coordinator+application_step.h b/ios/clean/chrome/app/steps/tab_grid_coordinator+application_step.h
deleted file mode 100644
index 341bb37f..0000000
--- a/ios/clean/chrome/app/steps/tab_grid_coordinator+application_step.h
+++ /dev/null
@@ -1,23 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef IOS_CLEAN_CHROME_APP_STEPS_TAB_GRID_COORDINATOR_APPLICATION_STEP_H_
-#define IOS_CLEAN_CHROME_APP_STEPS_TAB_GRID_COORDINATOR_APPLICATION_STEP_H_
-
-#import <UIKit/UIKit.h>
-
-#import "ios/clean/chrome/app/application_step.h"
-#import "ios/clean/chrome/browser/ui/tab_grid/tab_grid_coordinator.h"
-
-// Category on TabGridCoordinator to allow it to act as an application step
-// and control the root UI for the application when is starts.
-// Creates the main window and makes it key, but doesn't make it visible yet.
-//  Pre:  Application phase is APPLICATION_FOREGROUNDED and the main window is
-//        key.
-//  Post: Application phase is (still) APPLICATION_FOREGROUNDED, the main window
-//        is visible and has a root view controller set.
-@interface TabGridCoordinator (ApplicationStep)<ApplicationStep>
-@end
-
-#endif  // IOS_CLEAN_CHROME_APP_STEPS_TAB_GRID_COORDINATOR_APPLICATION_STEP_H_
diff --git a/ios/clean/chrome/browser/ui/find_in_page/BUILD.gn b/ios/clean/chrome/browser/ui/find_in_page/BUILD.gn
index 1274e30a..a53b364 100644
--- a/ios/clean/chrome/browser/ui/find_in_page/BUILD.gn
+++ b/ios/clean/chrome/browser/ui/find_in_page/BUILD.gn
@@ -48,3 +48,20 @@
   libs = [ "UIKit.framework" ]
   configs += [ "//build/config/compiler:enable_arc" ]
 }
+
+source_set("unit_tests") {
+  sources = [
+    "find_in_page_mediator_unittest.mm",
+  ]
+  deps = [
+    ":find_in_page",
+    ":find_in_page_ui",
+    "//base",
+    "//ios/chrome/browser/web_state_list",
+    "//ios/chrome/browser/web_state_list:test_support",
+    "//testing/gtest",
+    "//third_party/ocmock",
+  ]
+  configs += [ "//build/config/compiler:enable_arc" ]
+  testonly = true
+}
diff --git a/ios/clean/chrome/browser/ui/find_in_page/find_in_page_mediator.mm b/ios/clean/chrome/browser/ui/find_in_page/find_in_page_mediator.mm
index c3a269a..c5f7900 100644
--- a/ios/clean/chrome/browser/ui/find_in_page/find_in_page_mediator.mm
+++ b/ios/clean/chrome/browser/ui/find_in_page/find_in_page_mediator.mm
@@ -60,6 +60,10 @@
   return self;
 }
 
+- (void)dealloc {
+  _webStateList->RemoveObserver(_webStateListObserver.get());
+}
+
 - (void)stopFinding {
   web::WebState* webState = self.webStateList->GetActiveWebState();
   FindTabHelper* helper = FindTabHelper::FromWebState(webState);
diff --git a/ios/clean/chrome/browser/ui/find_in_page/find_in_page_mediator_unittest.mm b/ios/clean/chrome/browser/ui/find_in_page/find_in_page_mediator_unittest.mm
new file mode 100644
index 0000000..a147a2b
--- /dev/null
+++ b/ios/clean/chrome/browser/ui/find_in_page/find_in_page_mediator_unittest.mm
@@ -0,0 +1,42 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "ios/clean/chrome/browser/ui/find_in_page/find_in_page_mediator.h"
+
+#include "base/memory/ptr_util.h"
+#include "ios/chrome/browser/web_state_list/fake_web_state_list_delegate.h"
+#include "ios/chrome/browser/web_state_list/web_state_list.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "testing/platform_test.h"
+#import "third_party/ocmock/OCMock/OCMock.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+namespace {
+
+class FindInPageMediatorTest : public PlatformTest {
+ public:
+  FindInPageMediatorTest() {
+    web_state_list_ = base::MakeUnique<WebStateList>(&web_state_list_delegate_);
+    provider_ = [OCMockObject
+        niceMockForProtocol:@protocol(FindInPageConsumerProvider)];
+  }
+
+ protected:
+  std::unique_ptr<WebStateList> web_state_list_;
+  FakeWebStateListDelegate web_state_list_delegate_;
+  id provider_;
+};
+
+}  // namespace
+
+TEST_F(FindInPageMediatorTest, Init) {
+  FindInPageMediator* mediator =
+      [[FindInPageMediator alloc] initWithWebStateList:web_state_list_.get()
+                                              provider:provider_
+                                            dispatcher:nil];
+  [mediator self];
+}
diff --git a/ios/clean/chrome/browser/ui/root/BUILD.gn b/ios/clean/chrome/browser/ui/root/BUILD.gn
new file mode 100644
index 0000000..1eaadd7d
--- /dev/null
+++ b/ios/clean/chrome/browser/ui/root/BUILD.gn
@@ -0,0 +1,50 @@
+# 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.
+
+source_set("root") {
+  sources = [
+    "root_coordinator.h",
+    "root_coordinator.mm",
+  ]
+  deps = [
+    ":root_ui",
+    "//ios/clean/chrome/browser",
+    "//ios/clean/chrome/browser/ui/commands",
+    "//ios/clean/chrome/browser/ui/tab_grid",
+    "//ios/shared/chrome/browser/ui/browser_list",
+    "//ios/shared/chrome/browser/ui/commands",
+    "//ios/shared/chrome/browser/ui/coordinators",
+  ]
+  configs += [ "//build/config/compiler:enable_arc" ]
+}
+
+source_set("root_ui") {
+  sources = [
+    "root_container_view_controller.h",
+    "root_container_view_controller.mm",
+  ]
+  deps = [
+    "//base",
+    "//ios/clean/chrome/browser/ui/animators",
+  ]
+  configs += [ "//build/config/compiler:enable_arc" ]
+}
+
+source_set("unit_tests") {
+  sources = [
+    "root_container_view_controller_unittest.mm",
+    "root_coordinator_unittest.mm",
+  ]
+  deps = [
+    ":root",
+    ":root_ui",
+    "//base",
+    "//ios/clean/chrome/browser",
+    "//ios/clean/chrome/browser/ui/animators",
+    "//testing/gtest",
+    "//third_party/ocmock",
+  ]
+  configs += [ "//build/config/compiler:enable_arc" ]
+  testonly = true
+}
diff --git a/ios/clean/chrome/browser/ui/root/root_container_view_controller.h b/ios/clean/chrome/browser/ui/root/root_container_view_controller.h
new file mode 100644
index 0000000..f0693941
--- /dev/null
+++ b/ios/clean/chrome/browser/ui/root/root_container_view_controller.h
@@ -0,0 +1,21 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef IOS_CLEAN_CHROME_BROWSER_UI_ROOT_ROOT_CONTAINER_VIEW_CONTROLLER_H_
+#define IOS_CLEAN_CHROME_BROWSER_UI_ROOT_ROOT_CONTAINER_VIEW_CONTROLLER_H_
+
+#import <UIKit/UIKit.h>
+
+#import "ios/clean/chrome/browser/ui/animators/zoom_transition_delegate.h"
+
+// View controller that wholly contains a content view controller.
+@interface RootContainerViewController
+    : UIViewController<ZoomTransitionDelegate>
+
+// View controller showing the main content.
+@property(nonatomic, strong) UIViewController* contentViewController;
+
+@end
+
+#endif  // IOS_CLEAN_CHROME_BROWSER_UI_ROOT_ROOT_CONTAINER_VIEW_CONTROLLER_H_
diff --git a/ios/clean/chrome/browser/ui/root/root_container_view_controller.mm b/ios/clean/chrome/browser/ui/root/root_container_view_controller.mm
new file mode 100644
index 0000000..8a7467b
--- /dev/null
+++ b/ios/clean/chrome/browser/ui/root/root_container_view_controller.mm
@@ -0,0 +1,72 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "ios/clean/chrome/browser/ui/root/root_container_view_controller.h"
+
+#include "base/logging.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+@implementation RootContainerViewController
+@synthesize contentViewController = _contentViewController;
+
+#pragma mark - UIViewController
+
+- (void)viewDidLoad {
+  [super viewDidLoad];
+  self.view.backgroundColor = [UIColor blackColor];
+  [self attachChildViewController:self.contentViewController];
+}
+
+#pragma mark - Public properties
+
+- (void)setContentViewController:(UIViewController*)contentViewController {
+  if (self.contentViewController == contentViewController)
+    return;
+  if ([self isViewLoaded]) {
+    [self detachChildViewController:self.contentViewController];
+    [self attachChildViewController:contentViewController];
+  }
+  _contentViewController = contentViewController;
+}
+
+#pragma mark - ChildViewController helper methods
+
+- (void)attachChildViewController:(UIViewController*)viewController {
+  if (!viewController) {
+    return;
+  }
+  [self addChildViewController:viewController];
+  viewController.view.autoresizingMask =
+      UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
+  viewController.view.frame = self.view.bounds;
+  [self.view addSubview:viewController.view];
+  [viewController didMoveToParentViewController:self];
+}
+
+- (void)detachChildViewController:(UIViewController*)viewController {
+  if (!viewController) {
+    return;
+  }
+  DCHECK_EQ(self, viewController.parentViewController);
+  [viewController willMoveToParentViewController:nil];
+  [viewController.view removeFromSuperview];
+  [viewController removeFromParentViewController];
+}
+
+#pragma mark - ZoomTransitionDelegate
+
+- (CGRect)rectForZoomWithKey:(NSObject*)key inView:(UIView*)view {
+  if ([self.contentViewController
+          conformsToProtocol:@protocol(ZoomTransitionDelegate)]) {
+    return [static_cast<id<ZoomTransitionDelegate>>(self.contentViewController)
+        rectForZoomWithKey:key
+                    inView:view];
+  }
+  return CGRectNull;
+}
+
+@end
diff --git a/ios/clean/chrome/browser/ui/root/root_container_view_controller_unittest.mm b/ios/clean/chrome/browser/ui/root/root_container_view_controller_unittest.mm
new file mode 100644
index 0000000..ddb39002
--- /dev/null
+++ b/ios/clean/chrome/browser/ui/root/root_container_view_controller_unittest.mm
@@ -0,0 +1,88 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "ios/clean/chrome/browser/ui/root/root_container_view_controller.h"
+
+#include "base/macros.h"
+#import "ios/clean/chrome/browser/ui/animators/zoom_transition_delegate.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "testing/platform_test.h"
+#import "third_party/ocmock/OCMock/OCMock.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+namespace {
+
+class RootContainerViewControllerTest : public PlatformTest {
+ public:
+  RootContainerViewControllerTest() {
+    rootViewController_ = [[RootContainerViewController alloc] init];
+    contentViewController_ = [[UIViewController alloc] init];
+  }
+
+ protected:
+  RootContainerViewController* rootViewController_;
+  UIViewController* contentViewController_;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(RootContainerViewControllerTest);
+};
+
+// Tests that RootContainerViewController conforms to ZoomTransitionDelegate.
+TEST_F(RootContainerViewControllerTest, TestConformsToZoomTransitionDelegate) {
+  EXPECT_TRUE([RootContainerViewController
+      conformsToProtocol:@protocol(ZoomTransitionDelegate)]);
+}
+
+// Tests that calls to |rectForZoomWithKey| are forwarded to the
+// contentViewController.
+TEST_F(RootContainerViewControllerTest, TestForwardingMethod) {
+  id contentViewController = OCMProtocolMock(@protocol(ZoomTransitionDelegate));
+  rootViewController_.contentViewController = contentViewController;
+  [rootViewController_ rectForZoomWithKey:nil inView:nil];
+  [[contentViewController verify] rectForZoomWithKey:[OCMArg any]
+                                              inView:[OCMArg any]];
+}
+
+// Tests that there are no child view controllers before loading the view.
+TEST_F(RootContainerViewControllerTest, TestNotLoadingView) {
+  EXPECT_EQ(0u, rootViewController_.childViewControllers.count);
+  rootViewController_.contentViewController = contentViewController_;
+  EXPECT_EQ(0u, rootViewController_.childViewControllers.count);
+}
+
+// Tests setting the content view before loading the view controller.
+TEST_F(RootContainerViewControllerTest, TestSettingContentBeforeLoadingView) {
+  rootViewController_.contentViewController = contentViewController_;
+  EXPECT_EQ(0u, rootViewController_.childViewControllers.count);
+  [rootViewController_ loadViewIfNeeded];
+  EXPECT_EQ(1u, rootViewController_.childViewControllers.count);
+  EXPECT_EQ([rootViewController_.childViewControllers lastObject],
+            contentViewController_);
+}
+
+// Tests setting the content view after loading the view controller.
+TEST_F(RootContainerViewControllerTest, TestSettingContentAfterLoadingView) {
+  [rootViewController_ loadViewIfNeeded];
+  EXPECT_EQ(0u, rootViewController_.childViewControllers.count);
+  rootViewController_.contentViewController = contentViewController_;
+  EXPECT_EQ(1u, rootViewController_.childViewControllers.count);
+  EXPECT_EQ([rootViewController_.childViewControllers lastObject],
+            contentViewController_);
+}
+
+// Tests that content has changed properly.
+TEST_F(RootContainerViewControllerTest, TestChangingContent) {
+  rootViewController_.contentViewController = contentViewController_;
+  [rootViewController_ loadViewIfNeeded];
+  UIViewController* differentViewController = [[UIViewController alloc] init];
+  rootViewController_.contentViewController = differentViewController;
+  EXPECT_EQ(1u, rootViewController_.childViewControllers.count);
+  EXPECT_EQ([rootViewController_.childViewControllers lastObject],
+            differentViewController);
+}
+
+}  // namespace
diff --git a/ios/clean/chrome/browser/ui/root/root_coordinator.h b/ios/clean/chrome/browser/ui/root/root_coordinator.h
new file mode 100644
index 0000000..bbd8821
--- /dev/null
+++ b/ios/clean/chrome/browser/ui/root/root_coordinator.h
@@ -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.
+
+#ifndef IOS_CLEAN_CHROME_BROWSER_UI_ROOT_ROOT_COORDINATOR_H_
+#define IOS_CLEAN_CHROME_BROWSER_UI_ROOT_ROOT_COORDINATOR_H_
+
+#import "ios/clean/chrome/browser/url_opening.h"
+#import "ios/shared/chrome/browser/ui/coordinators/browser_coordinator.h"
+
+// Coordinator that runs the root container.
+@interface RootCoordinator : BrowserCoordinator<URLOpening>
+@end
+
+#endif  // IOS_CLEAN_CHROME_BROWSER_UI_ROOT_ROOT_COORDINATOR_H_
diff --git a/ios/clean/chrome/browser/ui/root/root_coordinator.mm b/ios/clean/chrome/browser/ui/root/root_coordinator.mm
new file mode 100644
index 0000000..af9bb884
--- /dev/null
+++ b/ios/clean/chrome/browser/ui/root/root_coordinator.mm
@@ -0,0 +1,70 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "ios/clean/chrome/browser/ui/root/root_coordinator.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+#import "ios/clean/chrome/browser/ui/root/root_container_view_controller.h"
+#import "ios/clean/chrome/browser/ui/tab_grid/tab_grid_coordinator.h"
+#import "ios/shared/chrome/browser/ui/browser_list/browser.h"
+#import "ios/shared/chrome/browser/ui/coordinators/browser_coordinator+internal.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+@interface RootCoordinator ()
+@property(nonatomic, strong) RootContainerViewController* viewController;
+@property(nonatomic, weak) TabGridCoordinator* tabGridCoordinator;
+@end
+
+@implementation RootCoordinator
+@synthesize viewController = _viewController;
+@synthesize tabGridCoordinator = _tabGridCoordinator;
+
+#pragma mark - BrowserCoordinator
+
+- (void)start {
+  self.viewController = [[RootContainerViewController alloc] init];
+
+  TabGridCoordinator* tabGridCoordinator = [[TabGridCoordinator alloc] init];
+  [self addChildCoordinator:tabGridCoordinator];
+  [tabGridCoordinator start];
+  self.tabGridCoordinator = tabGridCoordinator;
+
+  [super start];
+}
+
+- (void)stop {
+  [super stop];
+  // PLACEHOLDER: Stop child coordinators here for now. We might deal with this
+  // differently later on.
+  for (BrowserCoordinator* child in self.children) {
+    [child stop];
+    [self removeChildCoordinator:child];
+  }
+}
+
+- (void)childCoordinatorDidStart:(BrowserCoordinator*)childCoordinator {
+  self.viewController.contentViewController = childCoordinator.viewController;
+}
+
+- (void)childCoordinatorWillStop:(BrowserCoordinator*)childCoordinator {
+  self.viewController.contentViewController = nil;
+}
+
+- (BOOL)canAddOverlayCoordinator:(BrowserCoordinator*)overlayCoordinator {
+  return YES;
+}
+
+#pragma mark - URLOpening
+
+- (void)openURL:(NSURL*)URL {
+  [self.tabGridCoordinator openURL:URL];
+}
+
+@end
diff --git a/ios/clean/chrome/browser/ui/root/root_coordinator_unittest.mm b/ios/clean/chrome/browser/ui/root/root_coordinator_unittest.mm
new file mode 100644
index 0000000..db6a92a3
--- /dev/null
+++ b/ios/clean/chrome/browser/ui/root/root_coordinator_unittest.mm
@@ -0,0 +1,21 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "ios/clean/chrome/browser/ui/root/root_coordinator.h"
+
+#import "ios/clean/chrome/browser/url_opening.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+namespace {
+
+// Tests that RootCoordinator conforms to the URLOpening protocol.
+TEST(RootCoordinatorTest, TestConformsToURLOpening) {
+  EXPECT_TRUE([RootCoordinator conformsToProtocol:@protocol(URLOpening)]);
+}
+
+}  // namespace
diff --git a/ios/clean/chrome/browser/ui/tab_grid/tab_grid_coordinator.mm b/ios/clean/chrome/browser/ui/tab_grid/tab_grid_coordinator.mm
index 29f2180..60e799e5 100644
--- a/ios/clean/chrome/browser/ui/tab_grid/tab_grid_coordinator.mm
+++ b/ios/clean/chrome/browser/ui/tab_grid/tab_grid_coordinator.mm
@@ -88,6 +88,11 @@
 - (void)stop {
   [super stop];
   [self.browser->dispatcher() stopDispatchingToTarget:self];
+  [self.mediator disconnect];
+  for (BrowserCoordinator* child in self.children) {
+    [child stop];
+    [self removeChildCoordinator:child];
+  }
 }
 
 - (void)childCoordinatorDidStart:(BrowserCoordinator*)childCoordinator {
diff --git a/ios/clean/chrome/test/BUILD.gn b/ios/clean/chrome/test/BUILD.gn
index 904904a..9c6b6d5 100644
--- a/ios/clean/chrome/test/BUILD.gn
+++ b/ios/clean/chrome/test/BUILD.gn
@@ -19,11 +19,14 @@
     ios_packed_resources_target,
 
     # Add unit_tests target here.
+    "//ios/clean/chrome/app/steps:unit_tests",
     "//ios/clean/chrome/browser/ui/bookmarks:unit_tests",
     "//ios/clean/chrome/browser/ui/context_menu:unit_tests",
+    "//ios/clean/chrome/browser/ui/find_in_page:unit_tests",
     "//ios/clean/chrome/browser/ui/ntp:unit_tests",
     "//ios/clean/chrome/browser/ui/omnibox:unit_tests",
     "//ios/clean/chrome/browser/ui/recent_tabs:unit_tests",
+    "//ios/clean/chrome/browser/ui/root:unit_tests",
     "//ios/clean/chrome/browser/ui/tab_collection:unit_tests",
     "//ios/clean/chrome/browser/ui/tab_grid:unit_tests",
     "//ios/clean/chrome/browser/ui/toolbar:unit_tests",
diff --git a/ios/showcase/BUILD.gn b/ios/showcase/BUILD.gn
index 3dd7d51c..307589671 100644
--- a/ios/showcase/BUILD.gn
+++ b/ios/showcase/BUILD.gn
@@ -28,6 +28,7 @@
     "//ios/clean/chrome/browser/ui/tools:tools_ui",
     "//ios/showcase/content_suggestions",
     "//ios/showcase/payments",
+    "//ios/showcase/root",
     "//ios/showcase/settings",
     "//ios/showcase/tab",
     "//ios/showcase/tab_grid",
@@ -54,6 +55,7 @@
     "//ios/showcase/content_suggestions:eg_tests",
     "//ios/showcase/core:eg_tests",
     "//ios/showcase/payments:eg_tests",
+    "//ios/showcase/root:eg_tests",
     "//ios/showcase/tab:eg_tests",
     "//ios/showcase/tab_grid:eg_tests",
 
diff --git a/ios/showcase/common/coordinator.h b/ios/showcase/common/coordinator.h
index d044a03..335038c 100644
--- a/ios/showcase/common/coordinator.h
+++ b/ios/showcase/common/coordinator.h
@@ -5,7 +5,7 @@
 #ifndef IOS_SHOWCASE_COMMON_COORDINATOR_H_
 #define IOS_SHOWCASE_COMMON_COORDINATOR_H_
 
-#import <Foundation/Foundation.h>
+#import <UIKit/UIKit.h>
 
 // This protocol is the common interface to the simple non-production
 // coordinators that will initialize, set up and present production view
diff --git a/ios/showcase/core/showcase_model.mm b/ios/showcase/core/showcase_model.mm
index a4ddad547..83a4d18b 100644
--- a/ios/showcase/core/showcase_model.mm
+++ b/ios/showcase/core/showcase_model.mm
@@ -37,6 +37,11 @@
       showcase::kUseCaseKey : @"Payment request picker view",
     },
     @{
+      showcase::kClassForDisplayKey : @"RootContainerViewController",
+      showcase::kClassForInstantiationKey : @"SCRootCoordinator",
+      showcase::kUseCaseKey : @"Root container",
+    },
+    @{
       showcase::kClassForDisplayKey : @"SettingsViewController",
       showcase::kClassForInstantiationKey : @"SCSettingsCoordinator",
       showcase::kUseCaseKey : @"Main settings screen",
diff --git a/ios/showcase/root/BUILD.gn b/ios/showcase/root/BUILD.gn
new file mode 100644
index 0000000..64f5f25
--- /dev/null
+++ b/ios/showcase/root/BUILD.gn
@@ -0,0 +1,30 @@
+# 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.
+
+source_set("root") {
+  sources = [
+    "sc_root_coordinator.h",
+    "sc_root_coordinator.mm",
+  ]
+  deps = [
+    "//base",
+    "//ios/clean/chrome/browser/ui/root:root_ui",
+    "//ios/showcase/common",
+  ]
+  libs = [ "UIKit.framework" ]
+  configs += [ "//build/config/compiler:enable_arc" ]
+}
+
+source_set("eg_tests") {
+  testonly = true
+  sources = [
+    "sc_root_egtest.mm",
+  ]
+  deps = [
+    "//ios/showcase/test",
+    "//ios/third_party/earl_grey",
+  ]
+  libs = [ "XCTest.framework" ]
+  configs += [ "//build/config/compiler:enable_arc" ]
+}
diff --git a/ios/showcase/root/sc_root_coordinator.h b/ios/showcase/root/sc_root_coordinator.h
new file mode 100644
index 0000000..bbabf7d
--- /dev/null
+++ b/ios/showcase/root/sc_root_coordinator.h
@@ -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.
+
+#ifndef IOS_SHOWCASE_ROOT_SC_ROOT_COORDINATOR_H_
+#define IOS_SHOWCASE_ROOT_SC_ROOT_COORDINATOR_H_
+
+#import <Foundation/Foundation.h>
+
+#import "ios/showcase/common/navigation_coordinator.h"
+
+@interface SCRootCoordinator : NSObject<NavigationCoordinator>
+@end
+
+#endif  // IOS_SHOWCASE_ROOT_SC_ROOT_COORDINATOR_H_
diff --git a/ios/showcase/root/sc_root_coordinator.mm b/ios/showcase/root/sc_root_coordinator.mm
new file mode 100644
index 0000000..a837771
--- /dev/null
+++ b/ios/showcase/root/sc_root_coordinator.mm
@@ -0,0 +1,27 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "ios/showcase/root/sc_root_coordinator.h"
+
+#import "ios/clean/chrome/browser/ui/root/root_container_view_controller.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+@implementation SCRootCoordinator
+
+@synthesize baseViewController;
+
+- (void)start {
+  RootContainerViewController* viewController =
+      [[RootContainerViewController alloc] init];
+  UIViewController* contentViewController = [[UIViewController alloc] init];
+  contentViewController.view.backgroundColor = [UIColor greenColor];
+  viewController.contentViewController = contentViewController;
+  viewController.title = @"Root container";
+  [self.baseViewController pushViewController:viewController animated:YES];
+}
+
+@end
diff --git a/ios/showcase/root/sc_root_egtest.mm b/ios/showcase/root/sc_root_egtest.mm
new file mode 100644
index 0000000..4b3c26e
--- /dev/null
+++ b/ios/showcase/root/sc_root_egtest.mm
@@ -0,0 +1,26 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import <EarlGrey/EarlGrey.h>
+
+#import "ios/showcase/test/showcase_eg_utils.h"
+#import "ios/showcase/test/showcase_test_case.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+// Tests for the root container view controller.
+@interface SCRootTestCase : ShowcaseTestCase
+@end
+
+@implementation SCRootTestCase
+
+// Tests launching RootContainerViewController.
+- (void)testLaunch {
+  showcase_utils::Open(@"RootContainerViewController");
+  showcase_utils::Close();
+}
+
+@end
diff --git a/media/BUILD.gn b/media/BUILD.gn
index 71cfeb4..8d97ec5 100644
--- a/media/BUILD.gn
+++ b/media/BUILD.gn
@@ -324,11 +324,15 @@
       "filters/ffmpeg_demuxer.cc",
       "filters/ffmpeg_demuxer.h",
       "filters/ffmpeg_glue.cc",
-      "filters/ffmpeg_video_decoder.cc",
-      "filters/ffmpeg_video_decoder.h",
       "filters/in_memory_url_protocol.cc",
       "filters/in_memory_url_protocol.h",
     ]
+    if (!disable_ffmpeg_video_decoders) {
+      sources += [
+        "filters/ffmpeg_video_decoder.cc",
+        "filters/ffmpeg_video_decoder.h",
+      ]
+    }
     if (proprietary_codecs) {
       sources += [
         "filters/ffmpeg_aac_bitstream_converter.cc",
@@ -372,24 +376,21 @@
     deps += [ "//third_party/libvpx" ]
   }
 
-  if (is_android) {
-    # On Android, FFmpeg is built without video decoders. We only
-    # support hardware video decoding.
-    if (disable_ffmpeg_video_decoders) {
+  if (disable_ffmpeg_video_decoders) {
       sources -= [
-        "filters/ffmpeg_video_decoder.cc",
-        "filters/ffmpeg_video_decoder.h",
+        "filters/decrypting_video_decoder.cc",
+        "filters/decrypting_video_decoder.h",
       ]
-    }
-    sources += [
-      "filters/android/media_codec_audio_decoder.cc",
-      "filters/android/media_codec_audio_decoder.h",
-    ]
+  }
+
+  if (is_android) {
     sources -= [
       "filters/decrypting_audio_decoder.cc",
       "filters/decrypting_audio_decoder.h",
-      "filters/decrypting_video_decoder.cc",
-      "filters/decrypting_video_decoder.h",
+    ]
+    sources += [
+      "filters/android/media_codec_audio_decoder.cc",
+      "filters/android/media_codec_audio_decoder.h",
     ]
   }
 
@@ -624,7 +625,7 @@
 
   data_deps = []
 
-  if (is_android) {
+  if (disable_ffmpeg_video_decoders) {
     sources -= [
       "filters/decrypting_audio_decoder_unittest.cc",
       "filters/decrypting_video_decoder_unittest.cc",
@@ -657,9 +658,8 @@
       "//third_party/opus",
     ]
 
-    if (!is_android) {
+    if (!disable_ffmpeg_video_decoders) {
       sources += [
-        # FFmpeg on Android does not include video decoders.
         "filters/ffmpeg_video_decoder_unittest.cc",
       ]
     }
diff --git a/media/audio/cras/audio_manager_cras.cc b/media/audio/cras/audio_manager_cras.cc
index 85ff96e..a1d059e 100644
--- a/media/audio/cras/audio_manager_cras.cc
+++ b/media/audio/cras/audio_manager_cras.cc
@@ -16,6 +16,7 @@
 #include "base/nix/xdg_util.h"
 #include "base/stl_util.h"
 #include "base/strings/string_number_conversions.h"
+#include "base/sys_info.h"
 #include "chromeos/audio/audio_device.h"
 #include "chromeos/audio/cras_audio_handler.h"
 #include "media/audio/audio_device_description.h"
@@ -295,12 +296,23 @@
   return MakeInputStream(params, device_id);
 }
 
+int AudioManagerCras::GetMinimumOutputBufferSizePerBoard() {
+  // On faster boards we can use smaller buffer size for lower latency.
+  // On slower boards we should use larger buffer size to prevent underrun.
+  std::string board = base::SysInfo::GetLsbReleaseBoard();
+  if (board == "kevin")
+    return 768;
+  else if (board == "samus")
+    return 256;
+  return kMinimumOutputBufferSize;
+}
+
 AudioParameters AudioManagerCras::GetPreferredOutputStreamParameters(
     const std::string& output_device_id,
     const AudioParameters& input_params) {
   ChannelLayout channel_layout = CHANNEL_LAYOUT_STEREO;
   int sample_rate = kDefaultSampleRate;
-  int buffer_size = kMinimumOutputBufferSize;
+  int buffer_size = GetMinimumOutputBufferSizePerBoard();
   int bits_per_sample = 16;
   if (input_params.IsValid()) {
     sample_rate = input_params.sample_rate();
diff --git a/media/audio/cras/audio_manager_cras.h b/media/audio/cras/audio_manager_cras.h
index e5a4b5b..e50ad24 100644
--- a/media/audio/cras/audio_manager_cras.h
+++ b/media/audio/cras/audio_manager_cras.h
@@ -72,6 +72,9 @@
   AudioInputStream* MakeInputStream(const AudioParameters& params,
                                     const std::string& device_id);
 
+  // Get minimum output buffer size for this board.
+  int GetMinimumOutputBufferSizePerBoard();
+
   void GetAudioDeviceNamesImpl(bool is_input, AudioDeviceNames* device_names);
 
   void AddBeamformingDevices(AudioDeviceNames* device_names);
diff --git a/media/filters/decoder_selector.cc b/media/filters/decoder_selector.cc
index 3d6854f..c4271060 100644
--- a/media/filters/decoder_selector.cc
+++ b/media/filters/decoder_selector.cc
@@ -20,7 +20,7 @@
 #include "media/filters/decoder_stream_traits.h"
 #include "media/filters/decrypting_demuxer_stream.h"
 
-#if !defined(OS_ANDROID)
+#if !defined(DISABLE_FFMPEG_VIDEO_DECODERS)
 #include "media/filters/decrypting_audio_decoder.h"
 #include "media/filters/decrypting_video_decoder.h"
 #endif
@@ -96,7 +96,7 @@
   // When there is a CDM attached, always try the decrypting decoder or
   // demuxer-stream first.
   if (cdm_context_) {
-#if !defined(OS_ANDROID)
+#if !defined(DISABLE_FFMPEG_VIDEO_DECODERS)
     InitializeDecryptingDecoder();
 #else
     InitializeDecryptingDemuxerStream();
@@ -112,7 +112,7 @@
   InitializeDecoder();
 }
 
-#if !defined(OS_ANDROID)
+#if !defined(DISABLE_FFMPEG_VIDEO_DECODERS)
 template <DemuxerStream::Type StreamType>
 void DecoderSelector<StreamType>::InitializeDecryptingDecoder() {
   DVLOG(2) << __func__;
@@ -145,7 +145,7 @@
   // DecryptingDemuxerStream to do decrypt-only.
   InitializeDecryptingDemuxerStream();
 }
-#endif  // !defined(OS_ANDROID)
+#endif  // !defined(DISABLE_FFMPEG_VIDEO_DECODERS)
 
 template <DemuxerStream::Type StreamType>
 void DecoderSelector<StreamType>::InitializeDecryptingDemuxerStream() {
diff --git a/net/test/android/javatests/AndroidManifest.xml b/net/test/android/javatests/AndroidManifest.xml
index c2a2652..341949bc 100644
--- a/net/test/android/javatests/AndroidManifest.xml
+++ b/net/test/android/javatests/AndroidManifest.xml
@@ -19,7 +19,9 @@
         <service android:name="org.chromium.net.test.EmbeddedTestServerService"
                 android:exported="true"
                 tools:ignore="ExportedService">
-            <intent-filter android:action="org.chromium.net.test.EMBEDDED_TEST_SERVER_SERVICE" />
+            <intent-filter>
+                <action android:name="org.chromium.net.test.EMBEDDED_TEST_SERVER_SERVICE" />
+            </intent-filter>
         </service>
 
     </application>
diff --git a/skia/config/SkUserConfig.h b/skia/config/SkUserConfig.h
index 820fe5b9..9a2c463 100644
--- a/skia/config/SkUserConfig.h
+++ b/skia/config/SkUserConfig.h
@@ -220,6 +220,10 @@
 #define SK_DISABLE_DEFERRED_PROXIES
 #endif
 
+#ifndef SK_LEGACY_SWEEP_GRADIENT
+#define SK_LEGACY_SWEEP_GRADIENT
+#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/SlowTests b/third_party/WebKit/LayoutTests/SlowTests
index dca50cc5..54c968a 100644
--- a/third_party/WebKit/LayoutTests/SlowTests
+++ b/third_party/WebKit/LayoutTests/SlowTests
@@ -392,3 +392,6 @@
 # which can be very slow.
 crbug.com/678498 external/wpt/service-workers/service-worker/foreign-fetch-cors.https.html [ Slow ]
 crbug.com/678496 external/wpt/service-workers/service-worker/foreign-fetch-basics.https.html [ Slow ]
+
+# Slow on Debug
+crbug.com/710864 [ Debug ] editing/execCommand/delete-non-editable-range-crash.html [ Slow ]
diff --git a/third_party/WebKit/LayoutTests/svg/animations/target-move-relative-length-invalidation-crash-expected.txt b/third_party/WebKit/LayoutTests/svg/animations/target-move-relative-length-invalidation-crash-expected.txt
new file mode 100644
index 0000000..ddad5eaf
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/svg/animations/target-move-relative-length-invalidation-crash-expected.txt
@@ -0,0 +1 @@
+PASS if no crash
diff --git a/third_party/WebKit/LayoutTests/svg/animations/target-move-relative-length-invalidation-crash.html b/third_party/WebKit/LayoutTests/svg/animations/target-move-relative-length-invalidation-crash.html
new file mode 100644
index 0000000..7118f1f
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/svg/animations/target-move-relative-length-invalidation-crash.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<svg>
+  <g id="g1">
+    <text id="text1"/>
+  </g>
+  <g id="g2">
+    <text id="text2"/>
+    <animate values="100" href="#text2" attributeName="x" dur="4s"/>
+  </g>
+</svg>
+<p>PASS if no crash</p>
+<script>
+if (window.testRunner) {
+  testRunner.dumpAsText();
+  testRunner.waitUntilDone();
+}
+var g1 = document.getElementById("g1");
+var g2 = document.getElementById("g2");
+var text1 = document.getElementById("text1");
+
+window.onload = function() {
+  requestAnimationFrame(function() {
+    g1.remove();
+    text1.appendChild(document.querySelector("svg"));
+    g1.appendChild(g2);
+    if (window.testRunner)
+      testRunner.notifyDone();
+  });
+};
+</script>
diff --git a/third_party/WebKit/Source/core/svg/SVGElement.cpp b/third_party/WebKit/Source/core/svg/SVGElement.cpp
index e99a7e4..bf80cca 100644
--- a/third_party/WebKit/Source/core/svg/SVGElement.cpp
+++ b/third_party/WebKit/Source/core/svg/SVGElement.cpp
@@ -535,10 +535,17 @@
     SVGElement* client_element) {
   DCHECK(client_element);
 
+  // Through an unfortunate chain of events, we can end up calling this while a
+  // subtree is being removed, and before the subtree has been properly
+  // "disconnected". Hence check the entire ancestor chain to avoid propagating
+  // relative length clients up into ancestors that have already been
+  // disconnected.
   // If we're not yet in a document, this function will be called again from
   // insertedInto(). Do nothing now.
-  if (!isConnected())
-    return;
+  for (Node& current_node : NodeTraversal::InclusiveAncestorsOf(*this)) {
+    if (!current_node.isConnected())
+      return;
+  }
 
   // An element wants to notify us that its own relative lengths state changed.
   // Register it in the relative length map, and register us in the parent
diff --git a/third_party/WebKit/Source/platform/scheduler/base/task_queue_manager_unittest.cc b/third_party/WebKit/Source/platform/scheduler/base/task_queue_manager_unittest.cc
index 52fa223..8befec1 100644
--- a/third_party/WebKit/Source/platform/scheduler/base/task_queue_manager_unittest.cc
+++ b/third_party/WebKit/Source/platform/scheduler/base/task_queue_manager_unittest.cc
@@ -1730,26 +1730,30 @@
 TEST_F(TaskQueueManagerTest, TaskQueueObserver_DelayedTask) {
   Initialize(1u);
 
+  base::TimeTicks start_time = manager_->Delegate()->NowTicks();
+  base::TimeDelta delay10s(base::TimeDelta::FromSeconds(10));
+  base::TimeDelta delay100s(base::TimeDelta::FromSeconds(100));
+  base::TimeDelta delay1s(base::TimeDelta::FromSeconds(1));
+
   MockTaskQueueObserver observer;
   runners_[0]->SetObserver(&observer);
 
   // We should get a notification when a delayed task is posted on an empty
   // queue.
-  EXPECT_CALL(observer, OnQueueNextWakeUpChanged(runners_[0].get(), _));
-  runners_[0]->PostDelayedTask(FROM_HERE, base::Bind(&NopTask),
-                               base::TimeDelta::FromSeconds(10));
+  EXPECT_CALL(observer, OnQueueNextWakeUpChanged(runners_[0].get(),
+                                                 start_time + delay10s));
+  runners_[0]->PostDelayedTask(FROM_HERE, base::Bind(&NopTask), delay10s);
   Mock::VerifyAndClearExpectations(&observer);
 
   // We should not get a notification for a longer delay.
   EXPECT_CALL(observer, OnQueueNextWakeUpChanged(_, _)).Times(0);
-  runners_[0]->PostDelayedTask(FROM_HERE, base::Bind(&NopTask),
-                               base::TimeDelta::FromSeconds(100));
+  runners_[0]->PostDelayedTask(FROM_HERE, base::Bind(&NopTask), delay100s);
   Mock::VerifyAndClearExpectations(&observer);
 
   // We should get a notification for a shorter delay.
-  EXPECT_CALL(observer, OnQueueNextWakeUpChanged(runners_[0].get(), _));
-  runners_[0]->PostDelayedTask(FROM_HERE, base::Bind(&NopTask),
-                               base::TimeDelta::FromSeconds(1));
+  EXPECT_CALL(observer, OnQueueNextWakeUpChanged(runners_[0].get(),
+                                                 start_time + delay1s));
+  runners_[0]->PostDelayedTask(FROM_HERE, base::Bind(&NopTask), delay1s);
   Mock::VerifyAndClearExpectations(&observer);
 
   std::unique_ptr<TaskQueue::QueueEnabledVoter> voter =
@@ -1758,7 +1762,8 @@
 
   // When a queue has been enabled, we may get a notification if the
   // TimeDomain's next scheduled wake-up has changed.
-  EXPECT_CALL(observer, OnQueueNextWakeUpChanged(runners_[0].get(), _));
+  EXPECT_CALL(observer, OnQueueNextWakeUpChanged(runners_[0].get(),
+                                                 start_time + delay1s));
   voter->SetQueueEnabled(true);
 
   // Tidy up.
@@ -1772,14 +1777,18 @@
   runners_[0]->SetObserver(&observer);
   runners_[1]->SetObserver(&observer);
 
-  EXPECT_CALL(observer, OnQueueNextWakeUpChanged(runners_[0].get(), _))
+  base::TimeTicks start_time = manager_->Delegate()->NowTicks();
+  base::TimeDelta delay1s(base::TimeDelta::FromSeconds(1));
+  base::TimeDelta delay10s(base::TimeDelta::FromSeconds(10));
+
+  EXPECT_CALL(observer,
+              OnQueueNextWakeUpChanged(runners_[0].get(), start_time + delay1s))
       .Times(1);
-  EXPECT_CALL(observer, OnQueueNextWakeUpChanged(runners_[1].get(), _))
+  EXPECT_CALL(observer, OnQueueNextWakeUpChanged(runners_[1].get(),
+                                                 start_time + delay10s))
       .Times(1);
-  runners_[0]->PostDelayedTask(FROM_HERE, base::Bind(&NopTask),
-                               base::TimeDelta::FromSeconds(1));
-  runners_[1]->PostDelayedTask(FROM_HERE, base::Bind(&NopTask),
-                               base::TimeDelta::FromSeconds(10));
+  runners_[0]->PostDelayedTask(FROM_HERE, base::Bind(&NopTask), delay1s);
+  runners_[1]->PostDelayedTask(FROM_HERE, base::Bind(&NopTask), delay10s);
   testing::Mock::VerifyAndClearExpectations(&observer);
 
   std::unique_ptr<TaskQueue::QueueEnabledVoter> voter0 =
@@ -1793,7 +1802,8 @@
   Mock::VerifyAndClearExpectations(&observer);
 
   // Re-enabling it should should also trigger a notification.
-  EXPECT_CALL(observer, OnQueueNextWakeUpChanged(runners_[0].get(), _));
+  EXPECT_CALL(observer, OnQueueNextWakeUpChanged(runners_[0].get(),
+                                                 start_time + delay1s));
   voter0->SetQueueEnabled(true);
   Mock::VerifyAndClearExpectations(&observer);
 
@@ -1803,7 +1813,8 @@
   Mock::VerifyAndClearExpectations(&observer);
 
   // Re-enabling it should should trigger a notification.
-  EXPECT_CALL(observer, OnQueueNextWakeUpChanged(runners_[1].get(), _));
+  EXPECT_CALL(observer, OnQueueNextWakeUpChanged(runners_[1].get(),
+                                                 start_time + delay10s));
   voter1->SetQueueEnabled(true);
   Mock::VerifyAndClearExpectations(&observer);
 
@@ -1813,6 +1824,56 @@
   runners_[1]->UnregisterTaskQueue();
 }
 
+class CancelableTask {
+ public:
+  explicit CancelableTask(base::TickClock* clock)
+      : clock_(clock), weak_factory_(this) {}
+
+  void RecordTimeTask(std::vector<base::TimeTicks>* run_times) {
+    run_times->push_back(clock_->NowTicks());
+  }
+
+  base::TickClock* clock_;
+  base::WeakPtrFactory<CancelableTask> weak_factory_;
+};
+
+TEST_F(TaskQueueManagerTest, TaskQueueObserver_SweepCanceledDelayedTasks) {
+  Initialize(1u);
+
+  MockTaskQueueObserver observer;
+  runners_[0]->SetObserver(&observer);
+
+  base::TimeTicks start_time = manager_->Delegate()->NowTicks();
+  base::TimeDelta delay1(base::TimeDelta::FromSeconds(5));
+  base::TimeDelta delay2(base::TimeDelta::FromSeconds(10));
+
+  EXPECT_CALL(observer,
+              OnQueueNextWakeUpChanged(runners_[0].get(), start_time + delay1))
+      .Times(1);
+
+  CancelableTask task1(now_src_.get());
+  CancelableTask task2(now_src_.get());
+  std::vector<base::TimeTicks> run_times;
+  runners_[0]->PostDelayedTask(
+      FROM_HERE,
+      base::Bind(&CancelableTask::RecordTimeTask,
+                 task1.weak_factory_.GetWeakPtr(), &run_times),
+      delay1);
+  runners_[0]->PostDelayedTask(
+      FROM_HERE,
+      base::Bind(&CancelableTask::RecordTimeTask,
+                 task2.weak_factory_.GetWeakPtr(), &run_times),
+      delay2);
+
+  task1.weak_factory_.InvalidateWeakPtrs();
+
+  // Sweeping away canceled delayed tasks should trigger a notification.
+  EXPECT_CALL(observer,
+              OnQueueNextWakeUpChanged(runners_[0].get(), start_time + delay2))
+      .Times(1);
+  manager_->SweepCanceledDelayedTasks();
+}
+
 namespace {
 void ChromiumRunloopInspectionTask(
     scoped_refptr<cc::OrderedSimpleTaskRunner> test_task_runner) {
@@ -2167,19 +2228,6 @@
   EXPECT_EQ(2u, events.size());
 }
 
-class CancelableTask {
- public:
-  explicit CancelableTask(base::TickClock* clock)
-      : clock_(clock), weak_factory_(this) {}
-
-  void RecordTimeTask(std::vector<base::TimeTicks>* run_times) {
-    run_times->push_back(clock_->NowTicks());
-  }
-
-  base::TickClock* clock_;
-  base::WeakPtrFactory<CancelableTask> weak_factory_;
-};
-
 TEST_F(TaskQueueManagerTest, NoWakeUpsForCanceledDelayedTasks) {
   Initialize(1u);
 
diff --git a/third_party/qcms/BUILD.gn b/third_party/qcms/BUILD.gn
index 1183569b..183ae43 100644
--- a/third_party/qcms/BUILD.gn
+++ b/third_party/qcms/BUILD.gn
@@ -2,6 +2,8 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
+import("//testing/libfuzzer/fuzzer_test.gni")
+
 config("qcms_config") {
   include_dirs = [ "src" ]
 }
@@ -48,6 +50,23 @@
   }
 }
 
+# fuzzers BUILD depends on this target. Needed for package discovery
+group("fuzzers") {
+}
+
+fuzzer_test("qcms_color_space_fuzzer") {
+  sources = [
+    "//testing/libfuzzer/fuzzers/color_space_data.h",
+    "qcms_color_space_fuzzer.cc",
+  ]
+  deps = [
+    ":qcms",
+    "//base",
+  ]
+  dict = "//testing/libfuzzer/fuzzers/dicts/icc.dict"
+  libfuzzer_options = [ "max_len=4194304" ]
+}
+
 if (!disable_qcms) {
   executable("qcms_tests") {
     if (current_cpu == "x86" || current_cpu == "x64") {
diff --git a/third_party/qcms/DEPS b/third_party/qcms/DEPS
new file mode 100644
index 0000000..9fedf4a
--- /dev/null
+++ b/third_party/qcms/DEPS
@@ -0,0 +1,6 @@
+specific_include_rules = {
+  "qcms_color_space_fuzzer\.cc": [
+    "+base",
+    "+testing/libfuzzer/fuzzers",
+  ],
+}
diff --git a/third_party/qcms/qcms_color_space_fuzzer.cc b/third_party/qcms/qcms_color_space_fuzzer.cc
new file mode 100644
index 0000000..be08c88
--- /dev/null
+++ b/third_party/qcms/qcms_color_space_fuzzer.cc
@@ -0,0 +1,85 @@
+// 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 <cstddef>
+#include <cstdint>
+#include <random>
+
+#include "base/logging.h"
+#include "testing/libfuzzer/fuzzers/color_space_data.h"
+#include "third_party/qcms/src/qcms.h"
+
+static constexpr size_t kPixels = 2048 / 4;
+
+static uint32_t pixels[kPixels];
+
+static void GeneratePixels(size_t hash) {
+  static std::uniform_int_distribution<uint32_t> uniform(0u, ~0u);
+
+  std::mt19937_64 random(hash);
+  for (size_t i = 0; i < arraysize(pixels); ++i)
+    pixels[i] = uniform(random);
+}
+
+static qcms_profile* test;
+static qcms_profile* srgb;
+
+static void ColorTransform(bool input) {
+  if (!test)
+    return;
+
+  const qcms_intent intent = QCMS_INTENT_DEFAULT;
+  const qcms_data_type format = QCMS_DATA_RGBA_8;
+
+  auto* transform =
+      input ? qcms_transform_create(test, format, srgb, format, intent)
+            : qcms_transform_create(srgb, format, test, format, intent);
+  if (!transform)
+    return;
+
+  static uint32_t output[kPixels];
+
+  qcms_transform_data(transform, pixels, output, kPixels);
+  qcms_transform_release(transform);
+}
+
+static qcms_profile* SelectProfile(size_t hash) {
+  static qcms_profile* profiles[4] = {
+      qcms_profile_from_memory(kSRGBData, arraysize(kSRGBData)),
+      qcms_profile_from_memory(kSRGBPara, arraysize(kSRGBPara)),
+      qcms_profile_from_memory(kAdobeData, arraysize(kAdobeData)),
+      qcms_profile_sRGB(),
+  };
+
+  return profiles[hash & 3];
+}
+
+inline size_t Hash(const char* data, size_t size, size_t hash = ~0) {
+  for (size_t i = 0; i < size; ++i)
+    hash = hash * 131 + *data++;
+  return hash;
+}
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+  constexpr size_t kSizeLimit = 4 * 1024 * 1024;
+  if (size < 128 || size > kSizeLimit)
+    return 0;
+
+  test = qcms_profile_from_memory(data, size);
+  if (!test)
+    return 0;
+
+  const size_t hash = Hash(reinterpret_cast<const char*>(data), size);
+  srgb = SelectProfile(hash);
+  GeneratePixels(hash);
+
+  qcms_profile_precache_output_transform(srgb);
+  ColorTransform(true);
+
+  qcms_profile_precache_output_transform(test);
+  ColorTransform(false);
+
+  qcms_profile_release(test);
+  return 0;
+}
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml
index 2a960f1..9ea9c3b 100644
--- a/tools/metrics/histograms/histograms.xml
+++ b/tools/metrics/histograms/histograms.xml
@@ -41455,6 +41455,24 @@
   </summary>
 </histogram>
 
+<histogram name="NewTabPage.ContentSuggestions.ArticleFaviconFetchResult"
+    enum="FaviconFetchResult">
+  <owner>jkrcal@chromium.org</owner>
+  <summary>
+    Android: Result of fetching a favicon for an article suggestion on the New
+    Tab Page.
+  </summary>
+</histogram>
+
+<histogram name="NewTabPage.ContentSuggestions.ArticleFaviconFetchTime"
+    units="ms">
+  <owner>jkrcal@chromium.org</owner>
+  <summary>
+    Android: Time it takes to fetch a favicon for an article suggestion on the
+    New Tab Page.
+  </summary>
+</histogram>
+
 <histogram name="NewTabPage.ContentSuggestions.BackgroundFetchTrigger"
     enum="BackgroundFetchTrigger">
   <owner>jkrcal@chromium.org</owner>
@@ -83362,6 +83380,9 @@
   <int value="22" label="User joined maximum number of machines to the domain"/>
   <int value="23"
       label="kinit or smbclient failed to contact Key Distribution Center"/>
+  <int value="24" label="Kerberos credentials cache not found"/>
+  <int value="25" label="Kerberos ticket expired while renewing credentials"/>
+  <int value="26" label="klist exited with unspecified error"/>
 </enum>
 
 <enum name="AutocheckoutBubble" type="int">
@@ -93448,6 +93469,16 @@
   </int>
 </enum>
 
+<enum name="FaviconFetchResult" type="int">
+  <int value="0" label="SUCCESS_CACHED">
+    Success - favicon found in local cache
+  </int>
+  <int value="1" label="SUCCESS_FETCHED">
+    Success - favicon fetched from the server
+  </int>
+  <int value="2" label="FAILURE">Failure - favicon not available</int>
+</enum>
+
 <enum name="FeatureObserver" type="int">
 <!-- Generated from third_party/WebKit/Source/core/frame/UseCounter.h -->
 
diff --git a/ui/views/BUILD.gn b/ui/views/BUILD.gn
index 442155d..f6856dd 100644
--- a/ui/views/BUILD.gn
+++ b/ui/views/BUILD.gn
@@ -876,6 +876,7 @@
     "controls/native/native_view_host_unittest.cc",
     "controls/prefix_selector_unittest.cc",
     "controls/progress_bar_unittest.cc",
+    "controls/resize_area_unittest.cc",
     "controls/scroll_view_unittest.cc",
     "controls/scrollbar/scrollbar_unittest.cc",
     "controls/slider_unittest.cc",
diff --git a/ui/views/controls/resize_area.cc b/ui/views/controls/resize_area.cc
index 3698078..06c0325 100644
--- a/ui/views/controls/resize_area.cc
+++ b/ui/views/controls/resize_area.cc
@@ -14,9 +14,6 @@
 
 const char ResizeArea::kViewClassName[] = "ResizeArea";
 
-////////////////////////////////////////////////////////////////////////////////
-// ResizeArea
-
 ResizeArea::ResizeArea(ResizeAreaDelegate* delegate)
     : delegate_(delegate),
       initial_position_(0) {
@@ -34,17 +31,25 @@
                    : gfx::kNullCursor;
 }
 
+void ResizeArea::OnGestureEvent(ui::GestureEvent* event) {
+  if (event->type() == ui::ET_GESTURE_TAP_DOWN) {
+    SetInitialPosition(event->x());
+    event->SetHandled();
+  } else if (event->type() == ui::ET_GESTURE_SCROLL_BEGIN ||
+             event->type() == ui::ET_GESTURE_SCROLL_UPDATE) {
+    ReportResizeAmount(event->x(), false);
+    event->SetHandled();
+  } else if (event->type() == ui::ET_GESTURE_END) {
+    ReportResizeAmount(event->x(), true);
+    event->SetHandled();
+  }
+}
+
 bool ResizeArea::OnMousePressed(const ui::MouseEvent& event) {
   if (!event.IsOnlyLeftMouseButton())
     return false;
 
-  // The resize area obviously will move once you start dragging so we need to
-  // convert coordinates to screen coordinates so that we don't lose our
-  // bearings.
-  gfx::Point point(event.x(), 0);
-  View::ConvertPointToScreen(this, &point);
-  initial_position_ = point.x();
-
+  SetInitialPosition(event.x());
   return true;
 }
 
@@ -76,4 +81,10 @@
                       last_update);
 }
 
+void ResizeArea::SetInitialPosition(int event_x) {
+  gfx::Point point(event_x, 0);
+  View::ConvertPointToScreen(this, &point);
+  initial_position_ = point.x();
+}
+
 }  // namespace views
diff --git a/ui/views/controls/resize_area.h b/ui/views/controls/resize_area.h
index 171c80565..31dc6e3 100644
--- a/ui/views/controls/resize_area.h
+++ b/ui/views/controls/resize_area.h
@@ -14,11 +14,7 @@
 
 class ResizeAreaDelegate;
 
-////////////////////////////////////////////////////////////////////////////////
-//
 // An invisible area that acts like a horizontal resizer.
-//
-////////////////////////////////////////////////////////////////////////////////
 class VIEWS_EXPORT ResizeArea : public View {
  public:
   static const char kViewClassName[];
@@ -26,9 +22,10 @@
   explicit ResizeArea(ResizeAreaDelegate* delegate);
   ~ResizeArea() override;
 
-  // Overridden from views::View:
+  // views::View:
   const char* GetClassName() const override;
   gfx::NativeCursor GetCursor(const ui::MouseEvent& event) override;
+  void OnGestureEvent(ui::GestureEvent* event) override;
   bool OnMousePressed(const ui::MouseEvent& event) override;
   bool OnMouseDragged(const ui::MouseEvent& event) override;
   void OnMouseReleased(const ui::MouseEvent& event) override;
@@ -40,10 +37,16 @@
   // directionality.
   void ReportResizeAmount(int resize_amount, bool last_update);
 
+  // Converts |event_x| to screen coordinates and sets |initial_position_| to
+  // this value.
+  void SetInitialPosition(int event_x);
+
   // The delegate to notify when we have updates.
   ResizeAreaDelegate* delegate_;
 
-  // The mouse position at start (in screen coordinates).
+  // The event's x-position at the start of the resize operation. The resize
+  // area will move while being dragged, so |initial_position_| is represented
+  // in screen coordinates so that we don't lose our bearings.
   int initial_position_;
 
   DISALLOW_COPY_AND_ASSIGN(ResizeArea);
diff --git a/ui/views/controls/resize_area_delegate.h b/ui/views/controls/resize_area_delegate.h
index cc5e133d..ee5e57b 100644
--- a/ui/views/controls/resize_area_delegate.h
+++ b/ui/views/controls/resize_area_delegate.h
@@ -16,7 +16,7 @@
   // positive (depending on direction of dragging and flips according to
   // locale directionality: dragging to the left in LTR locales gives negative
   // |resize_amount| but positive amount for RTL). |done_resizing| is true if
-  // the user has released the mouse.
+  // the user has released the pointer (mouse, stylus, touch, etc.).
   virtual void OnResize(int resize_amount, bool done_resizing) = 0;
 
  protected:
diff --git a/ui/views/controls/resize_area_unittest.cc b/ui/views/controls/resize_area_unittest.cc
new file mode 100644
index 0000000..8d11df5e
--- /dev/null
+++ b/ui/views/controls/resize_area_unittest.cc
@@ -0,0 +1,200 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/views/controls/resize_area.h"
+
+#include <memory>
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/aura/window.h"
+#include "ui/events/test/event_generator.h"
+#include "ui/views/controls/resize_area_delegate.h"
+#include "ui/views/test/views_test_base.h"
+#include "ui/views/view.h"
+#include "ui/views/widget/widget.h"
+
+namespace {
+// Constants used by the ResizeAreaTest.SuccessfulGestureDrag test to simulate
+// a gesture drag by |kGestureScrollDistance| resulting from
+// |kGestureScrollSteps| ui::ET_GESTURE_SCROLL_UPDATE events being delivered.
+const int kGestureScrollDistance = 100;
+const int kGestureScrollSteps = 4;
+const int kDistancePerGestureScrollUpdate =
+    kGestureScrollDistance / kGestureScrollSteps;
+}
+
+namespace views {
+
+// Testing delegate used by ResizeAreaTest.
+class TestResizeAreaDelegate : public ResizeAreaDelegate {
+ public:
+  TestResizeAreaDelegate();
+  ~TestResizeAreaDelegate() override;
+
+  // ResizeAreaDelegate:
+  void OnResize(int resize_amount, bool done_resizing) override;
+
+  int resize_amount() { return resize_amount_; }
+  bool done_resizing() { return done_resizing_; }
+  bool on_resize_called() { return on_resize_called_; }
+
+ private:
+  int resize_amount_;
+  bool done_resizing_;
+  bool on_resize_called_;
+
+  DISALLOW_COPY_AND_ASSIGN(TestResizeAreaDelegate);
+};
+
+TestResizeAreaDelegate::TestResizeAreaDelegate()
+    : resize_amount_(0), done_resizing_(false), on_resize_called_(false) {}
+
+TestResizeAreaDelegate::~TestResizeAreaDelegate() {}
+
+void TestResizeAreaDelegate::OnResize(int resize_amount, bool done_resizing) {
+  resize_amount_ = resize_amount;
+  done_resizing_ = done_resizing;
+  on_resize_called_ = true;
+}
+
+// Test fixture for testing the ResizeArea class.
+class ResizeAreaTest : public ViewsTestBase {
+ public:
+  ResizeAreaTest();
+  ~ResizeAreaTest() override;
+
+  // Callback used by the SuccessfulGestureDrag test.
+  void ProcessGesture(ui::EventType type, const gfx::Vector2dF& delta);
+
+ protected:
+  // testing::Test:
+  void SetUp() override;
+  void TearDown() override;
+
+  ui::test::EventGenerator* event_generator() { return event_generator_.get(); }
+
+  int resize_amount() { return delegate_->resize_amount(); }
+  bool done_resizing() { return delegate_->done_resizing(); }
+  bool on_resize_called() { return delegate_->on_resize_called(); }
+  views::Widget* widget() { return widget_; }
+
+ private:
+  std::unique_ptr<TestResizeAreaDelegate> delegate_;
+  ResizeArea* resize_area_ = nullptr;
+  views::Widget* widget_ = nullptr;
+  std::unique_ptr<ui::test::EventGenerator> event_generator_;
+
+  // The number of ui::ET_GESTURE_SCROLL_UPDATE events seen by
+  // ProcessGesture().
+  int gesture_scroll_updates_seen_ = 0;
+
+  DISALLOW_COPY_AND_ASSIGN(ResizeAreaTest);
+};
+
+ResizeAreaTest::ResizeAreaTest() {}
+
+ResizeAreaTest::~ResizeAreaTest() {}
+
+void ResizeAreaTest::ProcessGesture(ui::EventType type,
+                                    const gfx::Vector2dF& delta) {
+  if (type == ui::ET_GESTURE_SCROLL_BEGIN) {
+    EXPECT_FALSE(done_resizing());
+    EXPECT_FALSE(on_resize_called());
+  } else if (type == ui::ET_GESTURE_SCROLL_UPDATE) {
+    gesture_scroll_updates_seen_++;
+    EXPECT_EQ(kDistancePerGestureScrollUpdate * gesture_scroll_updates_seen_,
+              resize_amount());
+    EXPECT_FALSE(done_resizing());
+    EXPECT_TRUE(on_resize_called());
+  } else if (type == ui::ET_GESTURE_SCROLL_END) {
+    EXPECT_TRUE(done_resizing());
+  }
+}
+
+void ResizeAreaTest::SetUp() {
+  views::ViewsTestBase::SetUp();
+
+  delegate_.reset(new TestResizeAreaDelegate);
+  resize_area_ = new ResizeArea(delegate_.get());
+
+  gfx::Size size(10, 10);
+  resize_area_->SetBounds(0, 0, size.width(), size.height());
+
+  views::Widget::InitParams init_params(
+      CreateParams(views::Widget::InitParams::TYPE_WINDOW_FRAMELESS));
+  init_params.bounds = gfx::Rect(size);
+
+  widget_ = new views::Widget();
+  widget_->Init(init_params);
+  widget_->SetContentsView(resize_area_);
+  widget_->Show();
+
+  event_generator_.reset(
+      new ui::test::EventGenerator(widget_->GetNativeWindow()));
+}
+
+void ResizeAreaTest::TearDown() {
+  if (widget_ && !widget_->IsClosed())
+    widget_->Close();
+
+  views::ViewsTestBase::TearDown();
+}
+
+// TODO(tdanderson): Enable these tests on OSX. See crbug.com/710475.
+#if !defined(OS_MACOSX)
+// Verifies the correct calls have been made to
+// TestResizeAreaDelegate::OnResize() for a sequence of mouse events
+// corresponding to a successful resize operation.
+TEST_F(ResizeAreaTest, SuccessfulMouseDrag) {
+  event_generator()->MoveMouseToCenterOf(widget()->GetNativeView());
+  event_generator()->PressLeftButton();
+
+  const int kFirstDragAmount = -5;
+  event_generator()->MoveMouseBy(kFirstDragAmount, 0);
+  EXPECT_EQ(kFirstDragAmount, resize_amount());
+  EXPECT_FALSE(done_resizing());
+  EXPECT_TRUE(on_resize_called());
+
+  const int kSecondDragAmount = 17;
+  event_generator()->MoveMouseBy(kSecondDragAmount, 0);
+  EXPECT_EQ(kFirstDragAmount + kSecondDragAmount, resize_amount());
+  EXPECT_FALSE(done_resizing());
+
+  event_generator()->ReleaseLeftButton();
+  EXPECT_EQ(kFirstDragAmount + kSecondDragAmount, resize_amount());
+  EXPECT_TRUE(done_resizing());
+}
+
+// Verifies that no resize is performed when attempting to resize using the
+// right mouse button.
+TEST_F(ResizeAreaTest, FailedMouseDrag) {
+  event_generator()->MoveMouseToCenterOf(widget()->GetNativeView());
+  event_generator()->PressRightButton();
+
+  const int kDragAmount = 18;
+  event_generator()->MoveMouseBy(kDragAmount, 0);
+  EXPECT_EQ(0, resize_amount());
+}
+
+// Verifies the correct calls have been made to
+// TestResizeAreaDelegate::OnResize() for a sequence of gesture events
+// corresponding to a successful resize operation.
+TEST_F(ResizeAreaTest, SuccessfulGestureDrag) {
+  gfx::Point start = widget()->GetNativeView()->bounds().CenterPoint();
+  event_generator()->GestureScrollSequenceWithCallback(
+      start, gfx::Point(start.x() + kGestureScrollDistance, start.y()),
+      base::TimeDelta::FromMilliseconds(200), kGestureScrollSteps,
+      base::Bind(&ResizeAreaTest::ProcessGesture, base::Unretained(this)));
+}
+
+// Verifies that no resize is performed on a gesture tap.
+TEST_F(ResizeAreaTest, NoDragOnGestureTap) {
+  event_generator()->GestureTapAt(
+      widget()->GetNativeView()->bounds().CenterPoint());
+
+  EXPECT_EQ(0, resize_amount());
+}
+#endif  // !defined(OS_MACOSX)
+
+}  // namespace views