diff --git a/DEPS b/DEPS
index 69ec89b..8894560 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': 'd4cbe5ee89a184263c5cb373c8380eb6126f56aa',
+  'v8_revision': '74cef60193dd753be696e7d839ed00fa07672a35',
   # 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.
@@ -64,7 +64,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling PDFium
   # and whatever else without interference from each other.
-  'pdfium_revision': 'b46ce4172ee8282a39997c175dff2bd8f187208f',
+  'pdfium_revision': '4183f201c5155717762df48e5d68330b754070f3',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling openmax_dl
   # and whatever else without interference from each other.
@@ -96,7 +96,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling catapult
   # and whatever else without interference from each other.
-  'catapult_revision': 'f3726edb45daae022d6bafdc2b69b46131894d99',
+  'catapult_revision': '72646aa580ee22cd0a37c2501a36198966d71dc5',
   # 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/ash/autoclick/mus/autoclick_application.cc b/ash/autoclick/mus/autoclick_application.cc
index b9e52e4..78040882 100644
--- a/ash/autoclick/mus/autoclick_application.cc
+++ b/ash/autoclick/mus/autoclick_application.cc
@@ -90,9 +90,13 @@
 AutoclickApplication::~AutoclickApplication() {}
 
 void AutoclickApplication::OnStart() {
-  aura_init_ = base::MakeUnique<views::AuraInit>(
+  aura_init_ = views::AuraInit::Create(
       context()->connector(), context()->identity(), "views_mus_resources.pak",
       std::string(), nullptr, views::AuraInit::Mode::AURA_MUS);
+  if (!aura_init_) {
+    context()->QuitNow();
+    return;
+  }
   autoclick_controller_common_.reset(new AutoclickControllerCommon(
       base::TimeDelta::FromMilliseconds(kDefaultAutoclickDelayMs), this));
 }
diff --git a/ash/mus/window_manager_application.cc b/ash/mus/window_manager_application.cc
index 274c231e..2f2d2ed 100644
--- a/ash/mus/window_manager_application.cc
+++ b/ash/mus/window_manager_application.cc
@@ -129,10 +129,14 @@
   mojo_interface_factory::RegisterInterfaces(
       &registry_, base::ThreadTaskRunnerHandle::Get());
 
-  aura_init_ = base::MakeUnique<views::AuraInit>(
+  aura_init_ = views::AuraInit::Create(
       context()->connector(), context()->identity(), "ash_mus_resources.pak",
       "ash_mus_resources_200.pak", nullptr,
       views::AuraInit::Mode::AURA_MUS_WINDOW_MANAGER);
+  if (!aura_init_) {
+    context()->QuitNow();
+    return;
+  }
   window_manager_ = base::MakeUnique<WindowManager>(
       context()->connector(), ash_config_, show_primary_host_on_connect_);
 
diff --git a/ash/touch_hud/mus/touch_hud_application.cc b/ash/touch_hud/mus/touch_hud_application.cc
index 59f138ad..7bdd417 100644
--- a/ash/touch_hud/mus/touch_hud_application.cc
+++ b/ash/touch_hud/mus/touch_hud_application.cc
@@ -69,9 +69,11 @@
 TouchHudApplication::~TouchHudApplication() {}
 
 void TouchHudApplication::OnStart() {
-  aura_init_ = base::MakeUnique<views::AuraInit>(
+  aura_init_ = views::AuraInit::Create(
       context()->connector(), context()->identity(), "views_mus_resources.pak",
       std::string(), nullptr, views::AuraInit::Mode::AURA_MUS);
+  if (!aura_init_)
+    context()->QuitNow();
 }
 
 void TouchHudApplication::OnBindInterface(
diff --git a/build/config/jumbo.gni b/build/config/jumbo.gni
index 4a5f22f..364485cb 100644
--- a/build/config/jumbo.gni
+++ b/build/config/jumbo.gni
@@ -66,6 +66,25 @@
   invoker_sources = invoker.sources
   gen_target_dir = get_path_info(invoker_sources[0], "gen_dir")
   assert(excluded_sources != [] || true)  # Prevent "unused variable".
+
+  # Find the gen_target_dir directory with shortest path. Short paths
+  # are nice in themselves since they mean shorter error messages and
+  # fewer bytes to parse, but the currently deployed version of ninja
+  # also has a limitation where it only allows 32 path components in
+  # Windows.
+  # See https://crbug.com/738186 and
+  # https://github.com/ninja-build/ninja/issues/1161
+  foreach(source_file, invoker.sources) {
+    possibly_better_gen_target_dir = get_path_info(gen_target_dir, "dir")
+    possibly_better_gen_target_dir_2 =
+        get_path_info(possibly_better_gen_target_dir, "dir")
+    alt_gen_target_dir = get_path_info(source_file, "gen_dir")
+    if (alt_gen_target_dir == possibly_better_gen_target_dir ||
+        alt_gen_target_dir == possibly_better_gen_target_dir_2) {
+      gen_target_dir = alt_gen_target_dir
+    }
+  }
+
   assert(gen_target_dir != "")  # Prevent "unused variable".
 
   if (use_jumbo_build_for_target) {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/signin/AccountSigninView.java b/chrome/android/java/src/org/chromium/chrome/browser/signin/AccountSigninView.java
index 0d87f0ed..00a6d2f0 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/signin/AccountSigninView.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/signin/AccountSigninView.java
@@ -242,6 +242,7 @@
 
         final List<String> oldAccountNames = mAccountNames;
         final AlertDialog updatingGmsDialog;
+        final long dialogShowTime = SystemClock.elapsedRealtime();
 
         if (mIsGooglePlayServicesOutOfDate) {
             updatingGmsDialog = new AlertDialog.Builder(getContext())
@@ -258,6 +259,8 @@
             public void onResult(List<String> result) {
                 if (updatingGmsDialog != null) {
                     updatingGmsDialog.dismiss();
+                    RecordHistogram.recordTimesHistogram("Signin.AndroidGmsUpdatingDialogShownTime",
+                            SystemClock.elapsedRealtime() - dialogShowTime, TimeUnit.MILLISECONDS);
                 }
                 mIsGooglePlayServicesOutOfDate = false;
 
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/ntp/NtpUiCaptureTestData.java b/chrome/android/javatests/src/org/chromium/chrome/browser/ntp/NtpUiCaptureTestData.java
index edf3a105..ffa3099 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/ntp/NtpUiCaptureTestData.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/ntp/NtpUiCaptureTestData.java
@@ -36,8 +36,8 @@
  */
 public class NtpUiCaptureTestData {
     private static final String[] FAKE_MOST_VISITED_TITLES =
-            new String[] {"Simple", "A longer label", "A label that should be truncated",
-                    "Dummy site", "Fake", "Not real", "Test", "Most visited 8"};
+            new String[] {"Queries", "Football, cricket, hockey, and more | Sports", "Meme Feed",
+                    "Facts", "The Morning News", "Tech", "Shop.rr", "Now Entertainment"};
 
     private static final String[] FAKE_MOST_VISITED_WHITELIST_ICON_PATHS =
             new String[] {"", "", "", "", "", "", "", ""};
@@ -46,47 +46,35 @@
             TileSource.TOP_SITES, TileSource.TOP_SITES, TileSource.TOP_SITES, TileSource.TOP_SITES,
             TileSource.TOP_SITES, TileSource.TOP_SITES, TileSource.TOP_SITES};
 
-    private static final String[] FAKE_MOST_VISITED_URLS = new String[] {
-            "junk1", "junk2", "junk3", "Dummy", "Fake", "Not real", "Test", "Most visited 8"};
+    private static final String[] FAKE_MOST_VISITED_URLS =
+            new String[] {"queries", "sports", "meme", "facts", "news", "tech", "shop", "movies"};
 
     private static final int[] FAKE_MOST_VISITED_COLORS = {Color.RED, Color.BLUE, Color.GREEN,
             Color.BLACK, Color.CYAN, Color.DKGRAY, Color.BLUE, Color.YELLOW};
 
     private static final SnippetArticle[] FAKE_ARTICLE_SUGGESTIONS = new SnippetArticle[] {
             new SnippetArticle(KnownCategories.ARTICLES, "suggestion0",
-                    "Lorem ipsum dolor sit amet, consectetur ", "Lorem Ipsum",
-                    "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod "
-                            + "tempor incididunt ut labore et dolore magna aliqua.",
-                    "junk1", getTimestamp(2017, Calendar.JUNE, 1), 0.0f, 0L, false),
-            new SnippetArticle(KnownCategories.ARTICLES, "suggestion1",
-                    "A title that just goes on, and on, and on; tells a complete story at great "
-                            + "length, and is simply too long.",
-                    "A publisher with a very long name", "preview", "junk1",
+                    "James Roderick to step down as conductor for Laville orchestra",
+                    "The Morning News", "summary is not used", "http://example.com",
+                    getTimestamp(2017, Calendar.JUNE, 1), 0.0f, 0L, false),
+            new SnippetArticle(KnownCategories.ARTICLES, "suggestion1", "Boy raises orphaned goat",
+                    "Meme feed", "summary is not used", "http://example.com",
+                    getTimestamp(2017, Calendar.JANUARY, 30), 0.0f, 0L, false),
+            new SnippetArticle(KnownCategories.ARTICLES, "suggestion2", "Top gigs this week",
+                    "Now Entertainment", "summary is not used", "http://example.com",
                     getTimestamp(2017, Calendar.JANUARY, 30), 0.0f, 0L, false)};
 
     private static final SnippetArticle[] FAKE_BOOKMARK_SUGGESTIONS = new SnippetArticle[] {
-            new SnippetArticle(KnownCategories.BOOKMARKS, "suggestion2",
-                    "Look, it’s very, very simple…. All I want… is a cup of tea.", "Publisher",
-                    "My attempt to get a teapot and kettle in London (y'know that country that "
-                            + "drinks staggering amounts of tea) ... should have been easy, "
-                            + "right?\n"
-                            + "\n"
-                            + "The ticket includes this gem:\n"
-                            + "--------\n"
-                            + " The concern with kettles is the fact that they are portable and "
-                            + "carry hot water.\n"
-                            + "\n"
-                            + "Yes, they are designed explicitly to do that, safely.\n"
-                            + "\n"
-                            + " There is a concern that staff are at risk to burns and scalds "
-                            + "when combining the two. \n"
-                            + "\n"
-                            + "Unlike the large machine that puts out pressurized steam ?\n",
-                    "junk1", getTimestamp(2017, Calendar.MARCH, 10), 0.0f, 0L, false),
-            new SnippetArticle(KnownCategories.BOOKMARKS, "bookmark1", "Title3", "Publisher",
-                    "preview", "junk1", getTimestamp(2017, Calendar.FEBRUARY, 20), 0.0f, 0L, false),
-            new SnippetArticle(KnownCategories.BOOKMARKS, "bookmark2", "Title4", "Publisher",
-                    "preview", "junk1", getTimestamp(2017, Calendar.MARCH, 30), 0.0f, 0L, false),
+            new SnippetArticle(KnownCategories.BOOKMARKS, "bookmark0",
+                    "Light pollution worse than ever", "Facts", "summary is not used",
+                    "http://example.com", getTimestamp(2017, Calendar.MARCH, 10), 0.0f, 0L, false),
+            new SnippetArticle(KnownCategories.BOOKMARKS, "bookmark1",
+                    "Emergency services suffering further budget cuts", "The Morning News",
+                    "summary is not used", "http://example.com",
+                    getTimestamp(2017, Calendar.FEBRUARY, 20), 0.0f, 0L, false),
+            new SnippetArticle(KnownCategories.BOOKMARKS, "bookmark2",
+                    "Local election yields surprise winner", "Facts", "summary is not used",
+                    "http://example.com", getTimestamp(2017, Calendar.MARCH, 30), 0.0f, 0L, false),
     };
 
     private static long getTimestamp(int year, int month, int day) {
@@ -110,15 +98,14 @@
         Assert.assertNotNull(favicon);
         fakeSuggestionsSource.setFaviconForId("suggestion0", favicon);
         fakeSuggestionsSource.setThumbnailForId("suggestion0",
-                BitmapFactory.decodeFile(UrlUtils.getTestFilePath("/android/google.png")));
+                BitmapFactory.decodeFile(
+                        UrlUtils.getTestFilePath("/android/UiCapture/conductor.jpg")));
         fakeSuggestionsSource.setThumbnailForId("suggestion1",
-                BitmapFactory.decodeFile(
-                        UrlUtils.getTestFilePath("/android/UiCapture/tower-bridge.jpg")));
+                BitmapFactory.decodeFile(UrlUtils.getTestFilePath("/android/UiCapture/goat.jpg")));
         fakeSuggestionsSource.setThumbnailForId("suggestion2",
-                BitmapFactory.decodeFile(
-                        UrlUtils.getTestFilePath("/android/UiCapture/kettle.jpg")));
+                BitmapFactory.decodeFile(UrlUtils.getTestFilePath("/android/UiCapture/gig.jpg")));
         fakeSuggestionsSource.setThumbnailForId("bookmark1",
-                BitmapFactory.decodeFile(UrlUtils.getTestFilePath("/android/UiCapture/frog.jpg")));
+                BitmapFactory.decodeFile(UrlUtils.getTestFilePath("/android/UiCapture/fire.jpg")));
         return fakeSuggestionsSource;
     }
 
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index 6e859f6..88edcb1 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -4593,7 +4593,6 @@
 
   deps += [
     "//components/metrics:test_support",
-    "//components/password_manager/content/common:mojo_interfaces",
     "//components/password_manager/core/browser:test_support",
     "//components/translate/content/common",
     "//skia",
diff --git a/chrome/browser/android/vr_shell/ui_elements/loading_indicator.cc b/chrome/browser/android/vr_shell/ui_elements/loading_indicator.cc
index 7a2add7..c255fae 100644
--- a/chrome/browser/android/vr_shell/ui_elements/loading_indicator.cc
+++ b/chrome/browser/android/vr_shell/ui_elements/loading_indicator.cc
@@ -61,9 +61,4 @@
   set_visible(enabled_ && (loading_ || visibility_timer_.IsRunning()));
 }
 
-void LoadingIndicator::OnBeginFrame(const base::TimeTicks& ticks) {
-  if (enabled_)
-    UpdateTexture();
-}
-
 }  // namespace vr_shell
diff --git a/chrome/browser/android/vr_shell/ui_elements/loading_indicator.h b/chrome/browser/android/vr_shell/ui_elements/loading_indicator.h
index 0b707a8..31c3c50 100644
--- a/chrome/browser/android/vr_shell/ui_elements/loading_indicator.h
+++ b/chrome/browser/android/vr_shell/ui_elements/loading_indicator.h
@@ -24,7 +24,6 @@
   void SetLoadProgress(float progress);
 
   void SetEnabled(bool enabled) override;
-  void OnBeginFrame(const base::TimeTicks& ticks) override;
 
  private:
   UiTexture* GetTexture() const override;
diff --git a/chrome/browser/android/vr_shell/ui_elements/textured_element.h b/chrome/browser/android/vr_shell/ui_elements/textured_element.h
index 3c009a0..848fe425 100644
--- a/chrome/browser/android/vr_shell/ui_elements/textured_element.h
+++ b/chrome/browser/android/vr_shell/ui_elements/textured_element.h
@@ -30,13 +30,14 @@
   void Render(UiElementRenderer* renderer,
               gfx::Transform view_proj_matrix) const final;
 
-  void OnBeginFrame(const base::TimeTicks& begin_frame_time) override;
-
  protected:
+  void UpdateTexture();
+
   virtual UiTexture* GetTexture() const = 0;
-  virtual void UpdateTexture();
   virtual void UpdateElementSize();
 
+  void OnBeginFrame(const base::TimeTicks& begin_frame_time) final;
+
  private:
   void Flush(SkSurface* surface);
   void OnSetMode() override;
diff --git a/chrome/browser/android/vr_shell/ui_elements/url_bar.cc b/chrome/browser/android/vr_shell/ui_elements/url_bar.cc
index 40dede3..18369ad 100644
--- a/chrome/browser/android/vr_shell/ui_elements/url_bar.cc
+++ b/chrome/browser/android/vr_shell/ui_elements/url_bar.cc
@@ -9,16 +9,6 @@
 
 namespace vr_shell {
 
-namespace {
-
-// We will often get spammed with many updates. We will also get security and
-// url updates out of sync. To address both these problems, we will hang onto
-// dirtyness for |kUpdateDelay| before updating our texture to reduce visual
-// churn.
-constexpr int64_t kUpdateDelayMS = 50;
-
-}  // namespace
-
 UrlBar::UrlBar(int preferred_width,
                const base::Callback<void()>& back_button_callback,
                const base::Callback<void()>& security_icon_callback,
@@ -30,11 +20,6 @@
 
 UrlBar::~UrlBar() = default;
 
-void UrlBar::UpdateTexture() {
-  TexturedElement::UpdateTexture();
-  last_update_time_ = last_begin_frame_time_;
-}
-
 UiTexture* UrlBar::GetTexture() const {
   return texture_.get();
 }
@@ -74,20 +59,6 @@
   return texture_->HitsUrlBar(position) || texture_->HitsBackButton(position);
 }
 
-void UrlBar::OnBeginFrame(const base::TimeTicks& begin_frame_time) {
-  last_begin_frame_time_ = begin_frame_time;
-  if (enabled_ && texture_->dirty()) {
-    int64_t delta_ms = (begin_frame_time - last_update_time_).InMilliseconds();
-    if (delta_ms > kUpdateDelayMS)
-      UpdateTexture();
-  }
-}
-
-void UrlBar::SetEnabled(bool enabled) {
-  enabled_ = enabled;
-  set_visible(enabled);
-}
-
 void UrlBar::SetToolbarState(const ToolbarState& state) {
   texture_->SetToolbarState(state);
 }
diff --git a/chrome/browser/android/vr_shell/ui_elements/url_bar.h b/chrome/browser/android/vr_shell/ui_elements/url_bar.h
index f5e116d..d7651bbd 100644
--- a/chrome/browser/android/vr_shell/ui_elements/url_bar.h
+++ b/chrome/browser/android/vr_shell/ui_elements/url_bar.h
@@ -35,26 +35,19 @@
   void OnButtonUp(const gfx::PointF& position) override;
   bool HitTest(const gfx::PointF& point) const override;
 
-  void OnBeginFrame(const base::TimeTicks& begin_frame_time) override;
-  void SetEnabled(bool enabled) override;
-
   void SetHistoryButtonsEnabled(bool can_go_back);
   void SetToolbarState(const ToolbarState& state);
 
  private:
-  void UpdateTexture() override;
   UiTexture* GetTexture() const override;
   void OnStateUpdated(const gfx::PointF& position);
 
   std::unique_ptr<UrlBarTexture> texture_;
   base::Callback<void()> back_button_callback_;
   base::Callback<void()> security_icon_callback_;
-  bool enabled_ = false;
   bool can_go_back_ = false;
   bool down_ = false;
   bool security_region_down_ = false;
-  base::TimeTicks last_begin_frame_time_;
-  base::TimeTicks last_update_time_;
 
   DISALLOW_COPY_AND_ASSIGN(UrlBar);
 };
diff --git a/chrome/browser/browsing_data/browsing_data_important_sites_util.cc b/chrome/browser/browsing_data/browsing_data_important_sites_util.cc
index 67cd278..fdc3182 100644
--- a/chrome/browser/browsing_data/browsing_data_important_sites_util.cc
+++ b/chrome/browser/browsing_data/browsing_data_important_sites_util.cc
@@ -64,13 +64,17 @@
   auto* observer =
       new BrowsingDataTaskObserver(remover, std::move(callback), 2);
 
-  int filterable_mask =
-      remove_mask &
-      ChromeBrowsingDataRemoverDelegate::IMPORTANT_SITES_DATA_TYPES;
-  int nonfilterable_mask =
-      remove_mask &
-      ~ChromeBrowsingDataRemoverDelegate::IMPORTANT_SITES_DATA_TYPES;
+  int filterable_mask = 0;
+  int nonfilterable_mask = remove_mask;
 
+  if (!filter_builder->IsEmptyBlacklist()) {
+    filterable_mask =
+        remove_mask &
+        ChromeBrowsingDataRemoverDelegate::IMPORTANT_SITES_DATA_TYPES;
+    nonfilterable_mask =
+        remove_mask &
+        ~ChromeBrowsingDataRemoverDelegate::IMPORTANT_SITES_DATA_TYPES;
+  }
   browsing_data::RecordDeletionForPeriod(time_period);
 
   if (filterable_mask) {
diff --git a/chrome/browser/chrome_browser_main_win.cc b/chrome/browser/chrome_browser_main_win.cc
index c5ec45e..50373fe 100644
--- a/chrome/browser/chrome_browser_main_win.cc
+++ b/chrome/browser/chrome_browser_main_win.cc
@@ -231,6 +231,16 @@
                  base::Unretained(module_database)));
 }
 
+void MaybePostSettingsResetPrompt() {
+  if (base::FeatureList::IsEnabled(safe_browsing::kSettingsResetPrompt)) {
+    content::BrowserThread::PostAfterStartupTask(
+        FROM_HERE,
+        content::BrowserThread::GetTaskRunnerForThread(
+            content::BrowserThread::UI),
+        base::Bind(safe_browsing::MaybeShowSettingsResetPromptWithDelay));
+  }
+}
+
 }  // namespace
 
 void ShowCloseBrowserFirstMessageBox() {
@@ -376,24 +386,20 @@
   InitializeChromeElf();
 
   // Reset settings for the current profile if it's tagged to be reset after a
-  // complete run of the Chrome Cleanup tool.
+  // complete run of the Chrome Cleanup tool. If post-cleanup settings reset is
+  // enabled, we delay checks for settings reset prompt until the scheduled
+  // reset is finished.
   if (safe_browsing::PostCleanupSettingsResetter::IsEnabled()) {
     // Using last opened profiles, because we want to find reset the profile
     // that was open in the last Chrome run, which may not be open yet in
     // the current run.
     safe_browsing::PostCleanupSettingsResetter().ResetTaggedProfiles(
         g_browser_process->profile_manager()->GetLastOpenedProfiles(),
-        base::BindOnce(&base::DoNothing),
+        base::BindOnce(&MaybePostSettingsResetPrompt),
         base::MakeUnique<
             safe_browsing::PostCleanupSettingsResetter::Delegate>());
-  }
-
-  if (base::FeatureList::IsEnabled(safe_browsing::kSettingsResetPrompt)) {
-    content::BrowserThread::PostAfterStartupTask(
-        FROM_HERE,
-        content::BrowserThread::GetTaskRunnerForThread(
-            content::BrowserThread::UI),
-        base::Bind(safe_browsing::MaybeShowSettingsResetPromptWithDelay));
+  } else {
+    MaybePostSettingsResetPrompt();
   }
 
   // Record UMA data about whether the fault-tolerant heap is enabled.
diff --git a/chrome/browser/chromeos/BUILD.gn b/chrome/browser/chromeos/BUILD.gn
index c3a53db..00a4d7d 100644
--- a/chrome/browser/chromeos/BUILD.gn
+++ b/chrome/browser/chromeos/BUILD.gn
@@ -282,6 +282,8 @@
     "app_mode/startup_app_launcher.h",
     "arc/accessibility/arc_accessibility_helper_bridge.cc",
     "arc/accessibility/arc_accessibility_helper_bridge.h",
+    "arc/accessibility/arc_accessibility_helper_bridge_factory.cc",
+    "arc/accessibility/arc_accessibility_helper_bridge_factory.h",
     "arc/accessibility/ax_tree_source_arc.cc",
     "arc/accessibility/ax_tree_source_arc.h",
     "arc/arc_auth_notification.cc",
@@ -1437,8 +1439,8 @@
     "system_logs/lsb_release_log_source.h",
     "system_logs/single_debug_daemon_log_source.cc",
     "system_logs/single_debug_daemon_log_source.h",
-    "system_logs/single_log_source.cc",
-    "system_logs/single_log_source.h",
+    "system_logs/single_log_file_log_source.cc",
+    "system_logs/single_log_file_log_source.h",
     "system_logs/touch_log_source.h",
     "system_logs/touch_log_source_ozone.cc",
     "system_logs/touch_log_source_x11.cc",
@@ -1868,7 +1870,7 @@
     "system/automatic_reboot_manager_unittest.cc",
     "system/device_disabling_manager_unittest.cc",
     "system_logs/single_debug_daemon_log_source_unittest.cc",
-    "system_logs/single_log_source_unittest.cc",
+    "system_logs/single_log_file_log_source_unittest.cc",
     "tether/tether_service_unittest.cc",
     "ui/accessibility_focus_ring_controller_unittest.cc",
     "ui/idle_app_name_notification_view_unittest.cc",
diff --git a/chrome/browser/chromeos/arc/accessibility/arc_accessibility_helper_bridge.cc b/chrome/browser/chromeos/arc/accessibility/arc_accessibility_helper_bridge.cc
index a227626..5855392 100644
--- a/chrome/browser/chromeos/arc/accessibility/arc_accessibility_helper_bridge.cc
+++ b/chrome/browser/chromeos/arc/accessibility/arc_accessibility_helper_bridge.cc
@@ -11,6 +11,7 @@
 #include "chrome/browser/profiles/profile.h"
 #include "chromeos/chromeos_switches.h"
 #include "components/arc/arc_bridge_service.h"
+#include "components/arc/arc_service_manager.h"
 #include "components/exo/shell_surface.h"
 #include "components/exo/surface.h"
 #include "ui/aura/client/aura_constants.h"
@@ -43,10 +44,11 @@
   return task_id;
 }
 
-void DispatchFocusChange(arc::mojom::AccessibilityNodeInfoData* node_data) {
+void DispatchFocusChange(arc::mojom::AccessibilityNodeInfoData* node_data,
+                         Profile* profile) {
   chromeos::AccessibilityManager* accessibility_manager =
       chromeos::AccessibilityManager::Get();
-  if (!accessibility_manager)
+  if (!accessibility_manager || accessibility_manager->profile() != profile)
     return;
 
   exo::WMHelper* wm_helper = exo::WMHelper::GetInstance();
@@ -66,7 +68,7 @@
   accessibility_manager->OnViewFocusedInArc(bounds_in_screen);
 }
 
-arc::mojom::AccessibilityFilterType GetFilterType() {
+arc::mojom::AccessibilityFilterType GetFilterTypeForProfile(Profile* profile) {
   if (base::CommandLine::ForCurrentProcess()->HasSwitch(
           chromeos::switches::kEnableChromeVoxArcSupport)) {
     return arc::mojom::AccessibilityFilterType::ALL;
@@ -77,6 +79,10 @@
   if (!accessibility_manager)
     return arc::mojom::AccessibilityFilterType::OFF;
 
+  // TODO(yawano): Support the case where primary user is in background.
+  if (accessibility_manager->profile() != profile)
+    return arc::mojom::AccessibilityFilterType::OFF;
+
   if (accessibility_manager->IsSpokenFeedbackEnabled())
     return arc::mojom::AccessibilityFilterType::WHITELISTED_PACKAGE_NAME;
 
@@ -91,57 +97,57 @@
 namespace arc {
 
 ArcAccessibilityHelperBridge::ArcAccessibilityHelperBridge(
-    ArcBridgeService* bridge_service)
-    : ArcService(bridge_service), binding_(this), current_task_id_(kNoTaskId) {
-  arc_bridge_service()->accessibility_helper()->AddObserver(this);
+    Profile* profile,
+    ArcBridgeService* arc_bridge_service)
+    : profile_(profile),
+      arc_bridge_service_(arc_bridge_service),
+      binding_(this),
+      current_task_id_(kNoTaskId) {
+  arc_bridge_service_->accessibility_helper()->AddObserver(this);
+
+  // Null on testing.
+  auto* app_list_prefs = ArcAppListPrefs::Get(profile_);
+  if (app_list_prefs)
+    app_list_prefs->AddObserver(this);
 }
 
-ArcAccessibilityHelperBridge::~ArcAccessibilityHelperBridge() {
+ArcAccessibilityHelperBridge::~ArcAccessibilityHelperBridge() = default;
+
+void ArcAccessibilityHelperBridge::Shutdown() {
   // We do not unregister ourselves from WMHelper as an ActivationObserver
   // because it is always null at this point during teardown.
 
-  chromeos::AccessibilityManager* accessibility_manager =
-      chromeos::AccessibilityManager::Get();
-  if (accessibility_manager) {
-    Profile* profile = accessibility_manager->profile();
-    if (profile && ArcAppListPrefs::Get(profile))
-      ArcAppListPrefs::Get(profile)->RemoveObserver(this);
-  }
+  // Null on testing.
+  auto* app_list_prefs = ArcAppListPrefs::Get(profile_);
+  if (app_list_prefs)
+    app_list_prefs->RemoveObserver(this);
 
-  ArcBridgeService* arc_bridge_service_ptr = arc_bridge_service();
-  if (arc_bridge_service_ptr)
-    arc_bridge_service_ptr->accessibility_helper()->RemoveObserver(this);
+  // TODO(hidehiko): Currently, the lifetime of ArcBridgeService and
+  // BrowserContextKeyedService is not nested.
+  // If ArcServiceManager::Get() returns nullptr, it is already destructed,
+  // so do not touch it.
+  if (ArcServiceManager::Get())
+    arc_bridge_service_->accessibility_helper()->RemoveObserver(this);
 }
 
 void ArcAccessibilityHelperBridge::OnInstanceReady() {
-  chromeos::AccessibilityManager* accessibility_manager =
-      chromeos::AccessibilityManager::Get();
-  if (!accessibility_manager)
-    return;
-
-  Profile* profile = accessibility_manager->profile();
-  if (!profile)
-    return;
-
-  ArcAppListPrefs* arc_app_list_prefs = ArcAppListPrefs::Get(profile);
-  if (!arc_app_list_prefs)
-    return;
-
-  arc_app_list_prefs->AddObserver(this);
   auto* instance = ARC_GET_INSTANCE_FOR_METHOD(
-      arc_bridge_service()->accessibility_helper(), Init);
+      arc_bridge_service_->accessibility_helper(), Init);
   DCHECK(instance);
 
   mojom::AccessibilityHelperHostPtr host_proxy;
   binding_.Bind(mojo::MakeRequest(&host_proxy));
   instance->Init(std::move(host_proxy));
 
-  arc::mojom::AccessibilityFilterType filter_type = GetFilterType();
+  arc::mojom::AccessibilityFilterType filter_type =
+      GetFilterTypeForProfile(profile_);
   instance->SetFilter(filter_type);
 
   if (filter_type == arc::mojom::AccessibilityFilterType::ALL ||
       filter_type ==
           arc::mojom::AccessibilityFilterType::WHITELISTED_PACKAGE_NAME) {
+    // TODO(yawano): Handle the case where filter_type has changed between
+    // OFF/FOCUS and ALL/WHITELISTED_PACKAGE_NAME after this initialization.
     exo::WMHelper::GetInstance()->AddActivationObserver(this);
   }
 }
@@ -150,12 +156,14 @@
     mojom::AccessibilityEventType event_type,
     mojom::AccessibilityNodeInfoDataPtr event_source) {
   if (event_type == arc::mojom::AccessibilityEventType::VIEW_FOCUSED)
-    DispatchFocusChange(event_source.get());
+    DispatchFocusChange(event_source.get(), profile_);
 }
 
 void ArcAccessibilityHelperBridge::OnAccessibilityEvent(
     mojom::AccessibilityEventDataPtr event_data) {
-  arc::mojom::AccessibilityFilterType filter_type = GetFilterType();
+  // TODO(yawano): Handle AccessibilityFilterType::OFF.
+  arc::mojom::AccessibilityFilterType filter_type =
+      GetFilterTypeForProfile(profile_);
 
   if (filter_type == arc::mojom::AccessibilityFilterType::ALL ||
       filter_type ==
@@ -203,7 +211,7 @@
     return;
 
   CHECK_EQ(1U, event_data.get()->node_data.size());
-  DispatchFocusChange(event_data.get()->node_data[0].get());
+  DispatchFocusChange(event_data.get()->node_data[0].get(), profile_);
 }
 
 void ArcAccessibilityHelperBridge::OnAction(
@@ -227,7 +235,7 @@
   }
 
   auto* instance = ARC_GET_INSTANCE_FOR_METHOD(
-      arc_bridge_service()->accessibility_helper(), PerformAction);
+      arc_bridge_service_->accessibility_helper(), PerformAction);
   instance->PerformAction(std::move(action_data));
 }
 
diff --git a/chrome/browser/chromeos/arc/accessibility/arc_accessibility_helper_bridge.h b/chrome/browser/chromeos/arc/accessibility/arc_accessibility_helper_bridge.h
index ebe0fd9..bf0f63f 100644
--- a/chrome/browser/chromeos/arc/accessibility/arc_accessibility_helper_bridge.h
+++ b/chrome/browser/chromeos/arc/accessibility/arc_accessibility_helper_bridge.h
@@ -12,32 +12,38 @@
 
 #include "chrome/browser/chromeos/arc/accessibility/ax_tree_source_arc.h"
 #include "chrome/browser/ui/app_list/arc/arc_app_list_prefs.h"
-#include "components/arc/arc_service.h"
 #include "components/arc/common/accessibility_helper.mojom.h"
 #include "components/arc/instance_holder.h"
 #include "components/exo/wm_helper.h"
+#include "components/keyed_service/core/keyed_service.h"
 #include "mojo/public/cpp/bindings/binding.h"
 #include "ui/accessibility/ax_host_delegate.h"
 
+class Profile;
+
 namespace arc {
 
-class ArcBridgeService;
 class AXTreeSourceArc;
+class ArcBridgeService;
 
 // ArcAccessibilityHelperBridge is an instance to receive converted Android
 // accessibility events and info via mojo interface and dispatch them to chrome
 // os components.
 class ArcAccessibilityHelperBridge
-    : public ArcService,
+    : public KeyedService,
       public mojom::AccessibilityHelperHost,
       public InstanceHolder<mojom::AccessibilityHelperInstance>::Observer,
       public exo::WMHelper::ActivationObserver,
       public AXTreeSourceArc::Delegate,
       public ArcAppListPrefs::Observer {
  public:
-  explicit ArcAccessibilityHelperBridge(ArcBridgeService* bridge_service);
+  ArcAccessibilityHelperBridge(Profile* profile,
+                               ArcBridgeService* arc_bridge_service);
   ~ArcAccessibilityHelperBridge() override;
 
+  // KeyedService overrides.
+  void Shutdown() override;
+
   // InstanceHolder<mojom::AccessibilityHelperInstance>::Observer overrides.
   void OnInstanceReady() override;
 
@@ -73,8 +79,9 @@
   void OnWindowActivated(aura::Window* gained_active,
                          aura::Window* lost_active) override;
 
+  Profile* const profile_;
+  ArcBridgeService* const arc_bridge_service_;
   mojo::Binding<mojom::AccessibilityHelperHost> binding_;
-
   std::map<std::string, std::unique_ptr<AXTreeSourceArc>> package_name_to_tree_;
   std::map<std::string, std::set<int32_t>> package_name_to_task_ids_;
   int32_t current_task_id_;
diff --git a/chrome/browser/chromeos/arc/accessibility/arc_accessibility_helper_bridge_factory.cc b/chrome/browser/chromeos/arc/accessibility/arc_accessibility_helper_bridge_factory.cc
new file mode 100644
index 0000000..f435893
--- /dev/null
+++ b/chrome/browser/chromeos/arc/accessibility/arc_accessibility_helper_bridge_factory.cc
@@ -0,0 +1,62 @@
+// 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/chromeos/arc/accessibility/arc_accessibility_helper_bridge_factory.h"
+
+#include "base/memory/singleton.h"
+#include "chrome/browser/chromeos/arc/accessibility/arc_accessibility_helper_bridge.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/app_list/arc/arc_app_list_prefs_factory.h"
+#include "components/arc/arc_service_manager.h"
+#include "components/keyed_service/content/browser_context_dependency_manager.h"
+
+namespace arc {
+
+// static
+ArcAccessibilityHelperBridge*
+ArcAccessibilityHelperBridgeFactory::GetForBrowserContext(
+    content::BrowserContext* context) {
+  return static_cast<ArcAccessibilityHelperBridge*>(
+      GetInstance()->GetServiceForBrowserContext(context, true /* create */));
+}
+
+// static
+ArcAccessibilityHelperBridgeFactory*
+ArcAccessibilityHelperBridgeFactory::GetInstance() {
+  return base::Singleton<ArcAccessibilityHelperBridgeFactory>::get();
+}
+
+ArcAccessibilityHelperBridgeFactory::ArcAccessibilityHelperBridgeFactory()
+    : BrowserContextKeyedServiceFactory(
+          "ArcAccessibilityHelperBridgeFactory",
+          BrowserContextDependencyManager::GetInstance()) {
+  // ArcAccessibilityHelperBridge needs to track task creation and destruction
+  // in the container, which are notified to ArcAppListPrefs via Mojo.
+  DependsOn(ArcAppListPrefsFactory::GetInstance());
+}
+
+ArcAccessibilityHelperBridgeFactory::~ArcAccessibilityHelperBridgeFactory() =
+    default;
+
+KeyedService* ArcAccessibilityHelperBridgeFactory::BuildServiceInstanceFor(
+    content::BrowserContext* context) const {
+  auto* arc_service_manager = arc::ArcServiceManager::Get();
+
+  // Practically, this is in testing case.
+  if (!arc_service_manager) {
+    VLOG(2) << "ArcServiceManager is not available.";
+    return nullptr;
+  }
+
+  if (arc_service_manager->browser_context() != context) {
+    VLOG(2) << "Non ARC allowed browser context.";
+    return nullptr;
+  }
+
+  return new ArcAccessibilityHelperBridge(
+      Profile::FromBrowserContext(context),
+      arc_service_manager->arc_bridge_service());
+}
+
+}  // namespace arc
diff --git a/chrome/browser/chromeos/arc/accessibility/arc_accessibility_helper_bridge_factory.h b/chrome/browser/chromeos/arc/accessibility/arc_accessibility_helper_bridge_factory.h
new file mode 100644
index 0000000..8dadd2a
--- /dev/null
+++ b/chrome/browser/chromeos/arc/accessibility/arc_accessibility_helper_bridge_factory.h
@@ -0,0 +1,49 @@
+// 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_CHROMEOS_ARC_ACCESSIBILITY_ARC_ACCESSIBILITY_HELPER_BRIDGE_FACTORY_H_
+#define CHROME_BROWSER_CHROMEOS_ARC_ACCESSIBILITY_ARC_ACCESSIBILITY_HELPER_BRIDGE_FACTORY_H_
+
+#include "base/macros.h"
+#include "components/keyed_service/content/browser_context_keyed_service_factory.h"
+
+namespace base {
+template <typename T>
+struct DefaultSingletonTraits;
+}  // namespace base
+
+namespace arc {
+
+class ArcAccessibilityHelperBridge;
+
+// Factory for ArcAccessibilityHelperBridge.
+class ArcAccessibilityHelperBridgeFactory
+    : public BrowserContextKeyedServiceFactory {
+ public:
+  // Returns the ArcAccessibilityHelperBridge for the given |context|,
+  // or nullptr if |context| is not allowed to use ARC.
+  // If the instance is not yet created, this creates a new service instance.
+  static ArcAccessibilityHelperBridge* GetForBrowserContext(
+      content::BrowserContext* context);
+
+  // Return the factory instance.
+  static ArcAccessibilityHelperBridgeFactory* GetInstance();
+
+ private:
+  friend struct base::DefaultSingletonTraits<
+      ArcAccessibilityHelperBridgeFactory>;
+
+  ArcAccessibilityHelperBridgeFactory();
+  ~ArcAccessibilityHelperBridgeFactory() override;
+
+  // BrowserContextKeyedServiceFactory overrides:
+  KeyedService* BuildServiceInstanceFor(
+      content::BrowserContext* context) const override;
+
+  DISALLOW_COPY_AND_ASSIGN(ArcAccessibilityHelperBridgeFactory);
+};
+
+}  // namespace arc
+
+#endif  // CHROME_BROWSER_CHROMEOS_ARC_ACCESSIBILITY_ARC_ACCESSIBILITY_HELPER_BRIDGE_FACTORY_H_
diff --git a/chrome/browser/chromeos/arc/accessibility/arc_accessibility_helper_bridge_unittest.cc b/chrome/browser/chromeos/arc/accessibility/arc_accessibility_helper_bridge_unittest.cc
index 277e8a18..da8bb254 100644
--- a/chrome/browser/chromeos/arc/accessibility/arc_accessibility_helper_bridge_unittest.cc
+++ b/chrome/browser/chromeos/arc/accessibility/arc_accessibility_helper_bridge_unittest.cc
@@ -8,10 +8,12 @@
 #include <utility>
 
 #include "base/command_line.h"
+#include "chrome/test/base/testing_profile.h"
 #include "chromeos/chromeos_switches.h"
 #include "components/arc/arc_bridge_service.h"
 #include "components/arc/common/accessibility_helper.mojom.h"
 #include "components/exo/wm_helper.h"
+#include "content/public/test/test_browser_thread_bundle.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/display/display.h"
 #include "ui/display/manager/managed_display_info.h"
@@ -57,9 +59,20 @@
   void SetUp() override {
     wm_helper_ = base::MakeUnique<FakeWMHelper>();
     exo::WMHelper::SetInstance(wm_helper_.get());
+    testing_profile_ = base::MakeUnique<TestingProfile>();
     bridge_service_ = base::MakeUnique<ArcBridgeService>();
     accessibility_helper_bridge_ =
-        base::MakeUnique<ArcAccessibilityHelperBridge>(bridge_service_.get());
+        base::MakeUnique<ArcAccessibilityHelperBridge>(testing_profile_.get(),
+                                                       bridge_service_.get());
+  }
+
+  void TearDown() override {
+    accessibility_helper_bridge_->Shutdown();
+    accessibility_helper_bridge_.reset();
+    bridge_service_.reset();
+    testing_profile_.reset();
+    exo::WMHelper::SetInstance(nullptr);
+    wm_helper_.reset();
   }
 
   ArcAccessibilityHelperBridge* accessibility_helper_bridge() {
@@ -67,7 +80,9 @@
   }
 
  private:
+  content::TestBrowserThreadBundle thread_bundle_;
   std::unique_ptr<FakeWMHelper> wm_helper_;
+  std::unique_ptr<TestingProfile> testing_profile_;
   std::unique_ptr<ArcBridgeService> bridge_service_;
   std::unique_ptr<ArcAccessibilityHelperBridge> accessibility_helper_bridge_;
 
diff --git a/chrome/browser/chromeos/arc/arc_service_launcher.cc b/chrome/browser/chromeos/arc/arc_service_launcher.cc
index 57a92c4..d3de137 100644
--- a/chrome/browser/chromeos/arc/arc_service_launcher.cc
+++ b/chrome/browser/chromeos/arc/arc_service_launcher.cc
@@ -10,7 +10,7 @@
 #include "base/logging.h"
 #include "base/memory/ptr_util.h"
 #include "chrome/browser/chromeos/app_mode/arc/arc_kiosk_app_service.h"
-#include "chrome/browser/chromeos/arc/accessibility/arc_accessibility_helper_bridge.h"
+#include "chrome/browser/chromeos/arc/accessibility/arc_accessibility_helper_bridge_factory.h"
 #include "chrome/browser/chromeos/arc/arc_play_store_enabled_preference_handler.h"
 #include "chrome/browser/chromeos/arc/arc_session_manager.h"
 #include "chrome/browser/chromeos/arc/arc_util.h"
@@ -95,8 +95,6 @@
 
   // List in lexicographical order.
   arc_service_manager_->AddService(
-      base::MakeUnique<ArcAccessibilityHelperBridge>(arc_bridge_service));
-  arc_service_manager_->AddService(
       base::MakeUnique<ArcAudioBridge>(arc_bridge_service));
   arc_service_manager_->AddService(
       base::MakeUnique<ArcAuthService>(arc_bridge_service));
@@ -185,6 +183,7 @@
     arc_session_manager_->Shutdown();
 
   arc_session_manager_->SetProfile(profile);
+  arc_service_manager_->set_browser_context(profile);
 }
 
 void ArcServiceLauncher::OnPrimaryUserProfilePrepared(Profile* profile) {
@@ -197,7 +196,11 @@
     return;
   }
 
-  // List in lexicographical order
+  // Instantiate ARC related BrowserContextKeyedService classes which need
+  // to be running at the beginning of the container run.
+  // List in lexicographical order.
+  ArcAccessibilityHelperBridgeFactory::GetForBrowserContext(profile);
+
   arc_service_manager_->AddService(base::MakeUnique<ArcBootPhaseMonitorBridge>(
       arc_service_manager_->arc_bridge_service(),
       multi_user_util::GetAccountIdFromProfile(profile)));
diff --git a/chrome/browser/chromeos/system_logs/single_log_source.cc b/chrome/browser/chromeos/system_logs/single_log_file_log_source.cc
similarity index 90%
rename from chrome/browser/chromeos/system_logs/single_log_source.cc
rename to chrome/browser/chromeos/system_logs/single_log_file_log_source.cc
index 005cb4ac..c2ea547 100644
--- a/chrome/browser/chromeos/system_logs/single_log_source.cc
+++ b/chrome/browser/chromeos/system_logs/single_log_file_log_source.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/chromeos/system_logs/single_log_source.h"
+#include "chrome/browser/chromeos/system_logs/single_log_file_log_source.h"
 
 #include "base/bind.h"
 #include "base/files/file_path.h"
@@ -34,13 +34,13 @@
 // base system log directory path. In the future, if non-file source types are
 // added, this function should return an empty file path.
 base::FilePath GetLogFileSourceRelativeFilePath(
-    SingleLogSource::SupportedSource source) {
+    SingleLogFileLogSource::SupportedSource source) {
   switch (source) {
-    case SingleLogSource::SupportedSource::kMessages:
+    case SingleLogFileLogSource::SupportedSource::kMessages:
       return base::FilePath("messages");
-    case SingleLogSource::SupportedSource::kUiLatest:
+    case SingleLogFileLogSource::SupportedSource::kUiLatest:
       return base::FilePath("ui/ui.LATEST");
-    case SingleLogSource::SupportedSource::kAtrusLog:
+    case SingleLogFileLogSource::SupportedSource::kAtrusLog:
       return base::FilePath("atrus.log");
   }
   NOTREACHED();
@@ -134,7 +134,7 @@
 
 }  // namespace
 
-SingleLogSource::SingleLogSource(SupportedSource source_type)
+SingleLogFileLogSource::SingleLogFileLogSource(SupportedSource source_type)
     : SystemLogsSource(GetLogFileSourceRelativeFilePath(source_type).value()),
       source_type_(source_type),
       log_file_dir_path_(kDefaultSystemLogDirPath),
@@ -142,15 +142,15 @@
       file_inode_(0),
       weak_ptr_factory_(this) {}
 
-SingleLogSource::~SingleLogSource() {}
+SingleLogFileLogSource::~SingleLogFileLogSource() {}
 
 // static
-void SingleLogSource::SetChromeStartTimeForTesting(
+void SingleLogFileLogSource::SetChromeStartTimeForTesting(
     const base::Time* start_time) {
   g_chrome_start_time_for_test = start_time;
 }
 
-void SingleLogSource::Fetch(const SysLogsSourceCallback& callback) {
+void SingleLogFileLogSource::Fetch(const SysLogsSourceCallback& callback) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
   DCHECK(!callback.is_null());
 
@@ -158,17 +158,18 @@
   base::PostTaskWithTraitsAndReply(
       FROM_HERE,
       base::TaskTraits(base::MayBlock(), base::TaskPriority::BACKGROUND),
-      base::Bind(&SingleLogSource::ReadFile, weak_ptr_factory_.GetWeakPtr(),
+      base::Bind(&SingleLogFileLogSource::ReadFile,
+                 weak_ptr_factory_.GetWeakPtr(),
                  kMaxNumAllowedLogRotationsDuringFileRead, response),
       base::Bind(callback, base::Owned(response)));
 }
 
-base::FilePath SingleLogSource::GetLogFilePath() const {
+base::FilePath SingleLogFileLogSource::GetLogFilePath() const {
   return base::FilePath(log_file_dir_path_).Append(source_name());
 }
 
-void SingleLogSource::ReadFile(size_t num_rotations_allowed,
-                               SystemLogsResponse* result) {
+void SingleLogFileLogSource::ReadFile(size_t num_rotations_allowed,
+                                      SystemLogsResponse* result) {
   // Attempt to open the file if it was not previously opened.
   if (!file_.IsValid()) {
     file_.Initialize(GetLogFilePath(),
diff --git a/chrome/browser/chromeos/system_logs/single_log_source.h b/chrome/browser/chromeos/system_logs/single_log_file_log_source.h
similarity index 82%
rename from chrome/browser/chromeos/system_logs/single_log_source.h
rename to chrome/browser/chromeos/system_logs/single_log_file_log_source.h
index 1a7c4c9..861c1ed 100644
--- a/chrome/browser/chromeos/system_logs/single_log_source.h
+++ b/chrome/browser/chromeos/system_logs/single_log_file_log_source.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROME_BROWSER_CHROMEOS_SYSTEM_LOGS_SINGLE_LOG_SOURCE_H_
-#define CHROME_BROWSER_CHROMEOS_SYSTEM_LOGS_SINGLE_LOG_SOURCE_H_
+#ifndef CHROME_BROWSER_CHROMEOS_SYSTEM_LOGS_SINGLE_LOG_FILE_LOG_SOURCE_H_
+#define CHROME_BROWSER_CHROMEOS_SYSTEM_LOGS_SINGLE_LOG_FILE_LOG_SOURCE_H_
 
 #include <stddef.h>
 #include <sys/types.h>
@@ -21,7 +21,7 @@
 namespace system_logs {
 
 // Gathers log data from a single source, possibly incrementally.
-class SingleLogSource : public SystemLogsSource {
+class SingleLogFileLogSource : public SystemLogsSource {
  public:
   enum class SupportedSource {
     // For /var/log/messages.
@@ -34,8 +34,8 @@
     kAtrusLog,
   };
 
-  explicit SingleLogSource(SupportedSource source);
-  ~SingleLogSource() override;
+  explicit SingleLogFileLogSource(SupportedSource source);
+  ~SingleLogFileLogSource() override;
 
   // During testing, use this to set a custom Chrome start time to override the
   // actual start time. Does not take ownership of |start_time|. Call this again
@@ -46,7 +46,7 @@
   void Fetch(const SysLogsSourceCallback& callback) override;
 
  private:
-  friend class SingleLogSourceTest;
+  friend class SingleLogFileLogSourceTest;
 
   // Returns the full path of the log file.
   base::FilePath GetLogFilePath() const;
@@ -85,11 +85,11 @@
   // For removing PII from log results.
   feedback::AnonymizerTool anonymizer_;
 
-  base::WeakPtrFactory<SingleLogSource> weak_ptr_factory_;
+  base::WeakPtrFactory<SingleLogFileLogSource> weak_ptr_factory_;
 
-  DISALLOW_COPY_AND_ASSIGN(SingleLogSource);
+  DISALLOW_COPY_AND_ASSIGN(SingleLogFileLogSource);
 };
 
 }  // namespace system_logs
 
-#endif  // CHROME_BROWSER_CHROMEOS_SYSTEM_LOGS_SINGLE_LOG_SOURCE_H_
+#endif  // CHROME_BROWSER_CHROMEOS_SYSTEM_LOGS_SINGLE_LOG_FILE_LOG_SOURCE_H_
diff --git a/chrome/browser/chromeos/system_logs/single_log_source_unittest.cc b/chrome/browser/chromeos/system_logs/single_log_file_log_source_unittest.cc
similarity index 82%
rename from chrome/browser/chromeos/system_logs/single_log_source_unittest.cc
rename to chrome/browser/chromeos/system_logs/single_log_file_log_source_unittest.cc
index 35ad0ee8..c8519f1 100644
--- a/chrome/browser/chromeos/system_logs/single_log_source_unittest.cc
+++ b/chrome/browser/chromeos/system_logs/single_log_file_log_source_unittest.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/chromeos/system_logs/single_log_source.h"
+#include "chrome/browser/chromeos/system_logs/single_log_file_log_source.h"
 
 #include <string>
 
@@ -38,17 +38,17 @@
 
 }  // namespace
 
-class SingleLogSourceTest : public ::testing::Test {
+class SingleLogFileLogSourceTest : public ::testing::Test {
  public:
-  SingleLogSourceTest()
+  SingleLogFileLogSourceTest()
       : scoped_task_environment_(
             base::test::ScopedTaskEnvironment::MainThreadType::UI),
         num_callback_calls_(0) {
     InitializeTestLogDir();
   }
 
-  ~SingleLogSourceTest() override {
-    SingleLogSource::SetChromeStartTimeForTesting(nullptr);
+  ~SingleLogFileLogSourceTest() override {
+    SingleLogFileLogSource::SetChromeStartTimeForTesting(nullptr);
   }
 
  protected:
@@ -72,8 +72,8 @@
 
   // Initializes the unit under test, |source_| to read a file from the dummy
   // system log directory.
-  void InitializeSource(SingleLogSource::SupportedSource source_type) {
-    source_ = base::MakeUnique<SingleLogSource>(source_type);
+  void InitializeSource(SingleLogFileLogSource::SupportedSource source_type) {
+    source_ = base::MakeUnique<SingleLogFileLogSource>(source_type);
     source_->log_file_dir_path_ = log_dir_.GetPath();
     log_file_path_ = source_->log_file_dir_path_.Append(source_->source_name());
     ASSERT_TRUE(base::PathExists(log_file_path_)) << log_file_path_.value();
@@ -108,8 +108,8 @@
   // Calls source_.Fetch() to start a logs fetch operation. Passes in
   // OnFileRead() as a callback. Runs until Fetch() has completed.
   void FetchFromSource() {
-    source_->Fetch(
-        base::Bind(&SingleLogSourceTest::OnFileRead, base::Unretained(this)));
+    source_->Fetch(base::Bind(&SingleLogFileLogSourceTest::OnFileRead,
+                              base::Unretained(this)));
     scoped_task_environment_.RunUntilIdle();
   }
 
@@ -141,7 +141,7 @@
   content::TestBrowserThreadBundle browser_thread_bundle_;
 
   // Unit under test.
-  std::unique_ptr<SingleLogSource> source_;
+  std::unique_ptr<SingleLogFileLogSource> source_;
 
   // Counts the number of times that |source_| has invoked the callback.
   int num_callback_calls_;
@@ -156,19 +156,19 @@
   // Path to the dummy log file in |log_dir_|.
   base::FilePath log_file_path_;
 
-  DISALLOW_COPY_AND_ASSIGN(SingleLogSourceTest);
+  DISALLOW_COPY_AND_ASSIGN(SingleLogFileLogSourceTest);
 };
 
-TEST_F(SingleLogSourceTest, EmptyFile) {
-  InitializeSource(SingleLogSource::SupportedSource::kMessages);
+TEST_F(SingleLogFileLogSourceTest, EmptyFile) {
+  InitializeSource(SingleLogFileLogSource::SupportedSource::kMessages);
   FetchFromSource();
 
   EXPECT_EQ(1, num_callback_calls());
   EXPECT_EQ("", latest_response());
 }
 
-TEST_F(SingleLogSourceTest, SingleRead) {
-  InitializeSource(SingleLogSource::SupportedSource::kUiLatest);
+TEST_F(SingleLogFileLogSourceTest, SingleRead) {
+  InitializeSource(SingleLogFileLogSource::SupportedSource::kUiLatest);
 
   EXPECT_TRUE(AppendToFile(base::FilePath("ui/ui.LATEST"), "Hello world!\n"));
   FetchFromSource();
@@ -177,8 +177,8 @@
   EXPECT_EQ("Hello world!\n", latest_response());
 }
 
-TEST_F(SingleLogSourceTest, IncrementalReads) {
-  InitializeSource(SingleLogSource::SupportedSource::kMessages);
+TEST_F(SingleLogFileLogSourceTest, IncrementalReads) {
+  InitializeSource(SingleLogFileLogSource::SupportedSource::kMessages);
 
   EXPECT_TRUE(AppendToFile(base::FilePath("messages"), "Hello world!\n"));
   FetchFromSource();
@@ -210,11 +210,11 @@
       file_contents);
 }
 
-// The log files read by SingleLogSource are not expected to be overwritten.
-// This test is just to ensure that the SingleLogSource class is robust enough
-// not to break in the event of an overwrite.
-TEST_F(SingleLogSourceTest, FileOverwrite) {
-  InitializeSource(SingleLogSource::SupportedSource::kUiLatest);
+// The log files read by SingleLogFileLogSource are not expected to be
+// overwritten. This test is just to ensure that the SingleLogFileLogSource
+// class is robust enough not to break in the event of an overwrite.
+TEST_F(SingleLogFileLogSourceTest, FileOverwrite) {
+  InitializeSource(SingleLogFileLogSource::SupportedSource::kUiLatest);
 
   EXPECT_TRUE(AppendToFile(base::FilePath("ui/ui.LATEST"), "0123456789\n"));
   FetchFromSource();
@@ -249,8 +249,8 @@
   EXPECT_EQ("yz\n", latest_response());
 }
 
-TEST_F(SingleLogSourceTest, IncompleteLines) {
-  InitializeSource(SingleLogSource::SupportedSource::kMessages);
+TEST_F(SingleLogFileLogSourceTest, IncompleteLines) {
+  InitializeSource(SingleLogFileLogSource::SupportedSource::kMessages);
 
   EXPECT_TRUE(AppendToFile(base::FilePath("messages"), "0123456789"));
   FetchFromSource();
@@ -286,8 +286,8 @@
   EXPECT_EQ("Goodbye world\n", latest_response());
 }
 
-TEST_F(SingleLogSourceTest, Anonymize) {
-  InitializeSource(SingleLogSource::SupportedSource::kUiLatest);
+TEST_F(SingleLogFileLogSourceTest, Anonymize) {
+  InitializeSource(SingleLogFileLogSource::SupportedSource::kUiLatest);
 
   EXPECT_TRUE(AppendToFile(base::FilePath("ui/ui.LATEST"),
                            "My MAC address is: 11:22:33:44:55:66\n"));
@@ -312,8 +312,8 @@
   EXPECT_EQ("Your MAC address is: ab:88:cd:00:00:02\n", latest_response());
 }
 
-TEST_F(SingleLogSourceTest, HandleLogFileRotation) {
-  InitializeSource(SingleLogSource::SupportedSource::kMessages);
+TEST_F(SingleLogFileLogSourceTest, HandleLogFileRotation) {
+  InitializeSource(SingleLogFileLogSource::SupportedSource::kMessages);
 
   EXPECT_TRUE(AppendToFile(base::FilePath("messages"), "1st log file\n"));
   FetchFromSource();
@@ -351,13 +351,13 @@
   EXPECT_EQ("Also no newline here...yet\n", latest_response());
 }
 
-TEST_F(SingleLogSourceTest, ReadRecentLinesFromMessages) {
+TEST_F(SingleLogFileLogSourceTest, ReadRecentLinesFromMessages) {
   // Write some lines to messages. Use various timestamp styles. Some lines have
   // timestamps, Some do not. All timestamps are in chronological order.
   const base::FilePath messages_path = base::FilePath("messages");
   // All timestamps below include time zone info. Some are EDT (-0400) and
-  // others are PDT (-0700). These make sure that SingleLogSource is able to
-  // read various standard timestamp formats.
+  // others are PDT (-0700). These make sure that SingleLogFileLogSource is able
+  // to read various standard timestamp formats.
   EXPECT_TRUE(
       AppendToFile(messages_path, "13 Jun 2017 15:00:00 -0400 : Alpha\n"));
   EXPECT_TRUE(
@@ -395,28 +395,28 @@
 
   // Provide a fake Chrome start time for testing: 15:00 EDT
   base::Time chrome_start_time = first_line_time;
-  SingleLogSource::SetChromeStartTimeForTesting(&chrome_start_time);
+  SingleLogFileLogSource::SetChromeStartTimeForTesting(&chrome_start_time);
 
   // Read from messages. The first line of messages should not have been read.
-  InitializeSource(SingleLogSource::SupportedSource::kMessages);
+  InitializeSource(SingleLogFileLogSource::SupportedSource::kMessages);
   FetchFromSource();
   EXPECT_EQ(1, num_callback_calls());
   EXPECT_EQ(10U, GetNumLinesInString(latest_response())) << latest_response();
 
   // Update Chrome start time to: 15:15 EDT
   chrome_start_time += base::TimeDelta::FromMinutes(15);
-  SingleLogSource::SetChromeStartTimeForTesting(&chrome_start_time);
+  SingleLogFileLogSource::SetChromeStartTimeForTesting(&chrome_start_time);
 
-  InitializeSource(SingleLogSource::SupportedSource::kMessages);
+  InitializeSource(SingleLogFileLogSource::SupportedSource::kMessages);
   FetchFromSource();
   EXPECT_EQ(2, num_callback_calls());
   EXPECT_EQ(9U, GetNumLinesInString(latest_response())) << latest_response();
 
   // Update Chrome start time: 15:45 EDT
   chrome_start_time += base::TimeDelta::FromMinutes(30);
-  SingleLogSource::SetChromeStartTimeForTesting(&chrome_start_time);
+  SingleLogFileLogSource::SetChromeStartTimeForTesting(&chrome_start_time);
 
-  InitializeSource(SingleLogSource::SupportedSource::kMessages);
+  InitializeSource(SingleLogFileLogSource::SupportedSource::kMessages);
   FetchFromSource();
   EXPECT_EQ(3, num_callback_calls());
   EXPECT_EQ(5U, GetNumLinesInString(latest_response())) << latest_response();
@@ -424,9 +424,9 @@
   // Update Chrome start time: 17:10 EDT
   chrome_start_time = first_line_time + base::TimeDelta::FromHours(2) +
                       base::TimeDelta::FromMinutes(10);
-  SingleLogSource::SetChromeStartTimeForTesting(&chrome_start_time);
+  SingleLogFileLogSource::SetChromeStartTimeForTesting(&chrome_start_time);
 
-  InitializeSource(SingleLogSource::SupportedSource::kMessages);
+  InitializeSource(SingleLogFileLogSource::SupportedSource::kMessages);
   FetchFromSource();
   EXPECT_EQ(4, num_callback_calls());
   EXPECT_EQ(4U, GetNumLinesInString(latest_response())) << latest_response();
@@ -434,18 +434,18 @@
   // Update Chrome start time: 17:40:00.125 EDT
   chrome_start_time +=
       base::TimeDelta::FromMinutes(30) + base::TimeDelta::FromMilliseconds(125);
-  SingleLogSource::SetChromeStartTimeForTesting(&chrome_start_time);
+  SingleLogFileLogSource::SetChromeStartTimeForTesting(&chrome_start_time);
 
-  InitializeSource(SingleLogSource::SupportedSource::kMessages);
+  InitializeSource(SingleLogFileLogSource::SupportedSource::kMessages);
   FetchFromSource();
   EXPECT_EQ(5, num_callback_calls());
   EXPECT_EQ(2U, GetNumLinesInString(latest_response())) << latest_response();
 
   // Update Chrome start time: 17:40:00.126 EDT
   chrome_start_time += base::TimeDelta::FromMilliseconds(1);
-  SingleLogSource::SetChromeStartTimeForTesting(&chrome_start_time);
+  SingleLogFileLogSource::SetChromeStartTimeForTesting(&chrome_start_time);
 
-  InitializeSource(SingleLogSource::SupportedSource::kMessages);
+  InitializeSource(SingleLogFileLogSource::SupportedSource::kMessages);
   FetchFromSource();
   EXPECT_EQ(6, num_callback_calls());
   EXPECT_EQ(1U, GetNumLinesInString(latest_response())) << latest_response();
@@ -453,18 +453,18 @@
   // Update Chrome start time: 18:10 EDT
   chrome_start_time = first_line_time + base::TimeDelta::FromHours(3) +
                       base::TimeDelta::FromMinutes(10);
-  SingleLogSource::SetChromeStartTimeForTesting(&chrome_start_time);
+  SingleLogFileLogSource::SetChromeStartTimeForTesting(&chrome_start_time);
 
-  InitializeSource(SingleLogSource::SupportedSource::kMessages);
+  InitializeSource(SingleLogFileLogSource::SupportedSource::kMessages);
   FetchFromSource();
   EXPECT_EQ(7, num_callback_calls());
   EXPECT_EQ(1U, GetNumLinesInString(latest_response())) << latest_response();
 
   // Update Chrome start time: 18:15 EDT
   chrome_start_time += base::TimeDelta::FromMinutes(5);
-  SingleLogSource::SetChromeStartTimeForTesting(&chrome_start_time);
+  SingleLogFileLogSource::SetChromeStartTimeForTesting(&chrome_start_time);
 
-  InitializeSource(SingleLogSource::SupportedSource::kMessages);
+  InitializeSource(SingleLogFileLogSource::SupportedSource::kMessages);
   FetchFromSource();
   EXPECT_EQ(8, num_callback_calls());
   EXPECT_EQ(0U, GetNumLinesInString(latest_response())) << latest_response();
diff --git a/chrome/browser/extensions/api/feedback_private/feedback_private_api_chromeos_unittest.cc b/chrome/browser/extensions/api/feedback_private/feedback_private_api_chromeos_unittest.cc
index 6764b7ec..ed67e16 100644
--- a/chrome/browser/extensions/api/feedback_private/feedback_private_api_chromeos_unittest.cc
+++ b/chrome/browser/extensions/api/feedback_private/feedback_private_api_chromeos_unittest.cc
@@ -18,12 +18,12 @@
 
 namespace {
 
+using api::feedback_private::LogSource;
 using api::feedback_private::ReadLogSourceResult;
 using api::feedback_private::ReadLogSourceParams;
 using base::TimeDelta;
-using system_logs::SingleLogSource;
 using system_logs::SystemLogsResponse;
-using SupportedSource = system_logs::SingleLogSource::SupportedSource;
+using system_logs::SystemLogsSource;
 
 std::unique_ptr<KeyedService> ApiResourceManagerTestFactory(
     content::BrowserContext* context) {
@@ -41,12 +41,12 @@
   return params_json_string;
 }
 
-// A dummy SingleLogSource that does not require real system logs to be
+// A dummy SystemLogsSource that does not require real system logs to be
 // available during testing.
-class TestSingleLogSource : public SingleLogSource {
+class TestSingleLogSource : public SystemLogsSource {
  public:
-  explicit TestSingleLogSource(SupportedSource type)
-      : SingleLogSource(type), call_count_(0) {}
+  explicit TestSingleLogSource(LogSource type)
+      : SystemLogsSource(ToString(type)), call_count_(0) {}
 
   ~TestSingleLogSource() override = default;
 
@@ -66,7 +66,7 @@
 
     // Do not directly pass the result to the callback, because that's not how
     // log sources actually work. Instead, simulate the asynchronous operation
-    // of a SingleLogSource by invoking the callback separately.
+    // of a SystemLogsSource by invoking the callback separately.
     base::ThreadTaskRunnerHandle::Get()->PostTask(
         FROM_HERE, base::BindOnce(callback, base::Owned(result_map)));
   }
@@ -74,7 +74,7 @@
   // Instantiates a new instance of this class. Does not retain ownership. Used
   // to create a Callback that can be used to override the default behavior of
   // SingleLogSourceFactory.
-  static std::unique_ptr<SingleLogSource> Create(SupportedSource type) {
+  static std::unique_ptr<SystemLogsSource> Create(LogSource type) {
     return base::MakeUnique<TestSingleLogSource>(type);
   }
 
diff --git a/chrome/browser/extensions/api/feedback_private/log_source_access_manager.cc b/chrome/browser/extensions/api/feedback_private/log_source_access_manager.cc
index 3784e2f..a71167e3 100644
--- a/chrome/browser/extensions/api/feedback_private/log_source_access_manager.cc
+++ b/chrome/browser/extensions/api/feedback_private/log_source_access_manager.cc
@@ -20,9 +20,7 @@
 namespace {
 
 namespace feedback_private = api::feedback_private;
-using feedback_private::LogSource;
-using SingleLogSource = system_logs::SingleLogSource;
-using SupportedSource = system_logs::SingleLogSource::SupportedSource;
+
 using SystemLogsResponse = system_logs::SystemLogsResponse;
 
 const int kMaxReadersPerSource = 10;
@@ -40,22 +38,6 @@
              : base::TimeDelta::FromMilliseconds(kDefaultRateLimitingTimeoutMs);
 }
 
-// Converts from feedback_private::LogSource to SupportedSource.
-SupportedSource GetSupportedSourceType(LogSource source) {
-  switch (source) {
-    case feedback_private::LOG_SOURCE_MESSAGES:
-      return SupportedSource::kMessages;
-    case feedback_private::LOG_SOURCE_UILATEST:
-      return SupportedSource::kUiLatest;
-    case feedback_private::LOG_SOURCE_NONE:
-    default:
-      NOTREACHED() << "Unknown log source type.";
-      return SingleLogSource::SupportedSource::kMessages;
-  }
-  NOTREACHED();
-  return SingleLogSource::SupportedSource::kMessages;
-}
-
 // SystemLogsResponse is a map of strings -> strings. The map value has the
 // actual log contents, a string containing all lines, separated by newlines.
 // This function extracts the individual lines and converts them into a vector
@@ -155,7 +137,7 @@
 }
 
 LogSourceAccessManager::SourceAndExtension::SourceAndExtension(
-    api::feedback_private::LogSource source,
+    feedback_private::LogSource source,
     const std::string& extension_id)
     : source(source), extension_id(extension_id) {}
 
@@ -173,8 +155,7 @@
   std::unique_ptr<LogSourceResource> new_resource =
       base::MakeUnique<LogSourceResource>(
           key.extension_id,
-          SingleLogSourceFactory::CreateSingleLogSource(
-              GetSupportedSourceType(key.source)),
+          SingleLogSourceFactory::CreateSingleLogSource(key.source),
           base::Bind(&LogSourceAccessManager::RemoveSource,
                      weak_factory_.GetWeakPtr(), key));
 
@@ -206,7 +187,7 @@
 }
 
 size_t LogSourceAccessManager::GetNumActiveResourcesForSource(
-    api::feedback_private::LogSource source) const {
+    feedback_private::LogSource source) const {
   size_t count = 0;
   // The stored entries are sorted first by source type, then by extension ID.
   // We can take advantage of this fact to avoid iterating over all elements.
diff --git a/chrome/browser/extensions/api/feedback_private/log_source_access_manager_chromeos_unittest.cc b/chrome/browser/extensions/api/feedback_private/log_source_access_manager_chromeos_unittest.cc
index acef8a77..9e7f466 100644
--- a/chrome/browser/extensions/api/feedback_private/log_source_access_manager_chromeos_unittest.cc
+++ b/chrome/browser/extensions/api/feedback_private/log_source_access_manager_chromeos_unittest.cc
@@ -19,10 +19,10 @@
 
 using api::feedback_private::LOG_SOURCE_MESSAGES;
 using api::feedback_private::LOG_SOURCE_UILATEST;
+using api::feedback_private::LogSource;
 using api::feedback_private::ReadLogSourceResult;
 using api::feedback_private::ReadLogSourceParams;
-using system_logs::SingleLogSource;
-using SupportedSource = system_logs::SingleLogSource::SupportedSource;
+using system_logs::SystemLogsSource;
 
 std::unique_ptr<KeyedService> ApiResourceManagerTestFactory(
     content::BrowserContext* context) {
@@ -32,11 +32,12 @@
 // Dummy function used as a callback for FetchFromSource().
 void OnFetchedFromSource(const ReadLogSourceResult& result) {}
 
-// A dummy SingleLogSource that does not require real system logs to be
+// A dummy SystemLogsSource that does not require real system logs to be
 // available during testing. Always returns an empty result.
-class EmptySingleLogSource : public SingleLogSource {
+class EmptySingleLogSource : public system_logs::SystemLogsSource {
  public:
-  explicit EmptySingleLogSource(SupportedSource type) : SingleLogSource(type) {}
+  explicit EmptySingleLogSource(LogSource type)
+      : SystemLogsSource(api::feedback_private::ToString(type)) {}
 
   ~EmptySingleLogSource() override = default;
 
@@ -47,7 +48,7 @@
 
     // Do not directly pass the result to the callback, because that's not how
     // log sources actually work. Instead, simulate the asynchronous operation
-    // of a SingleLogSource by invoking the callback separately.
+    // of a SystemLogsSource by invoking the callback separately.
     base::ThreadTaskRunnerHandle::Get()->PostTask(
         FROM_HERE, base::BindOnce(callback, base::Owned(result_map)));
   }
@@ -55,7 +56,7 @@
   // Instantiates a new instance of this class. Does not retain ownership. Used
   // to create a Callback that can be used to override the default behavior of
   // SingleLogSourceFactory.
-  static std::unique_ptr<SingleLogSource> Create(SupportedSource type) {
+  static std::unique_ptr<SystemLogsSource> Create(LogSource type) {
     return base::MakeUnique<EmptySingleLogSource>(type);
   }
 
diff --git a/chrome/browser/extensions/api/feedback_private/log_source_resource.cc b/chrome/browser/extensions/api/feedback_private/log_source_resource.cc
index b7e59e1..b416a5f 100644
--- a/chrome/browser/extensions/api/feedback_private/log_source_resource.cc
+++ b/chrome/browser/extensions/api/feedback_private/log_source_resource.cc
@@ -22,7 +22,7 @@
 
 LogSourceResource::LogSourceResource(
     const std::string& extension_id,
-    std::unique_ptr<system_logs::SingleLogSource> source,
+    std::unique_ptr<system_logs::SystemLogsSource> source,
     base::Closure unregister_callback)
     : ApiResource(extension_id),
       source_(source.release()),
diff --git a/chrome/browser/extensions/api/feedback_private/log_source_resource.h b/chrome/browser/extensions/api/feedback_private/log_source_resource.h
index 9a80dc68..02c1a4e 100644
--- a/chrome/browser/extensions/api/feedback_private/log_source_resource.h
+++ b/chrome/browser/extensions/api/feedback_private/log_source_resource.h
@@ -9,14 +9,14 @@
 
 #include "base/callback_helpers.h"
 #include "base/macros.h"
-#include "chrome/browser/chromeos/system_logs/single_log_source.h"
 #include "chrome/browser/extensions/api/feedback_private/log_source_access_manager.h"
+#include "chrome/browser/feedback/system_logs/system_logs_fetcher.h"
 #include "extensions/browser/api/api_resource.h"
 #include "extensions/browser/api/api_resource_manager.h"
 
 namespace extensions {
 
-// Holds a SingleLogSource object that is used by an extension using the
+// Holds a SystemLogsSource object that is used by an extension using the
 // feedbackPrivate API.
 class LogSourceResource : public ApiResource {
  public:
@@ -24,18 +24,18 @@
       content::BrowserThread::UI;
 
   LogSourceResource(const std::string& extension_id,
-                    std::unique_ptr<system_logs::SingleLogSource> source,
+                    std::unique_ptr<system_logs::SystemLogsSource> source,
                     base::Closure unregister_callback_);
 
   ~LogSourceResource() override;
 
-  system_logs::SingleLogSource* GetLogSource() const { return source_.get(); }
+  system_logs::SystemLogsSource* GetLogSource() const { return source_.get(); }
 
  private:
   friend class ApiResourceManager<LogSourceResource>;
   static const char* service_name() { return "LogSourceResource"; }
 
-  std::unique_ptr<system_logs::SingleLogSource> source_;
+  std::unique_ptr<system_logs::SystemLogsSource> source_;
 
   // This unregisters the LogSourceResource from a LogSourceAccessManager when
   // this resource is cleaned up. Just pass in a base::Closure to the
diff --git a/chrome/browser/extensions/api/feedback_private/single_log_source_factory.cc b/chrome/browser/extensions/api/feedback_private/single_log_source_factory.cc
index 86875ba0..f4b2e24 100644
--- a/chrome/browser/extensions/api/feedback_private/single_log_source_factory.cc
+++ b/chrome/browser/extensions/api/feedback_private/single_log_source_factory.cc
@@ -5,23 +5,40 @@
 #include "chrome/browser/extensions/api/feedback_private/single_log_source_factory.h"
 
 #include "base/memory/ptr_util.h"
+#include "chrome/browser/chromeos/system_logs/single_log_file_log_source.h"
 
 namespace extensions {
 
 namespace {
 
-using system_logs::SingleLogSource;
+namespace feedback_private = api::feedback_private;
+
+using system_logs::SingleLogFileLogSource;
+using system_logs::SystemLogsSource;
 
 SingleLogSourceFactory::CreateCallback* g_callback = nullptr;
 
 }  // namespace
 
 // static
-std::unique_ptr<SingleLogSource> SingleLogSourceFactory::CreateSingleLogSource(
-    SingleLogSource::SupportedSource type) {
+std::unique_ptr<SystemLogsSource> SingleLogSourceFactory::CreateSingleLogSource(
+    feedback_private::LogSource source_type) {
   if (g_callback)
-    return g_callback->Run(type);
-  return base::MakeUnique<SingleLogSource>(type);
+    return g_callback->Run(source_type);
+
+  switch (source_type) {
+    case feedback_private::LOG_SOURCE_MESSAGES:
+      return base::MakeUnique<system_logs::SingleLogFileLogSource>(
+          SingleLogFileLogSource::SupportedSource::kMessages);
+    case feedback_private::LOG_SOURCE_UILATEST:
+      return base::MakeUnique<system_logs::SingleLogFileLogSource>(
+          SingleLogFileLogSource::SupportedSource::kUiLatest);
+    case feedback_private::LOG_SOURCE_NONE:
+    default:
+      NOTREACHED() << "Unknown log source type.";
+      break;
+  }
+  return std::unique_ptr<SystemLogsSource>(nullptr);
 }
 
 // static
diff --git a/chrome/browser/extensions/api/feedback_private/single_log_source_factory.h b/chrome/browser/extensions/api/feedback_private/single_log_source_factory.h
index 6d7e710..feb2943 100644
--- a/chrome/browser/extensions/api/feedback_private/single_log_source_factory.h
+++ b/chrome/browser/extensions/api/feedback_private/single_log_source_factory.h
@@ -8,22 +8,23 @@
 #include <memory>
 
 #include "base/callback.h"
-#include "chrome/browser/chromeos/system_logs/single_log_source.h"
+#include "chrome/browser/feedback/system_logs/system_logs_fetcher.h"
+#include "chrome/common/extensions/api/feedback_private.h"
 
 namespace extensions {
 
-// Provides a way to override the creation of a new SingleLogSource during
+// Provides a way to override the creation of a new Single*LogSource during
 // testing.
 class SingleLogSourceFactory {
  public:
   using CreateCallback =
-      base::Callback<std::unique_ptr<system_logs::SingleLogSource>(
-          system_logs::SingleLogSource::SupportedSource)>;
+      base::Callback<std::unique_ptr<system_logs::SystemLogsSource>(
+          api::feedback_private::LogSource)>;
 
-  // Returns a SingleLogSource with source type of |type|. The caller must take
-  // ownership of the returned object.
-  static std::unique_ptr<system_logs::SingleLogSource> CreateSingleLogSource(
-      system_logs::SingleLogSource::SupportedSource type);
+  // Returns a Single*LogSource with source type corresponding to |type|. The
+  // caller must takeownership of the returned object.
+  static std::unique_ptr<system_logs::SystemLogsSource> CreateSingleLogSource(
+      api::feedback_private::LogSource type);
 
   // Pass in a callback that gets executed instead of the default behavior of
   // CreateSingleLogSource. Does not take ownership of |callback|. When done
diff --git a/chrome/browser/extensions/api/preference/preference_apitest.cc b/chrome/browser/extensions/api/preference/preference_apitest.cc
index fccee05..69e7e5d 100644
--- a/chrome/browser/extensions/api/preference/preference_apitest.cc
+++ b/chrome/browser/extensions/api/preference/preference_apitest.cc
@@ -25,6 +25,7 @@
 #include "components/content_settings/core/common/pref_names.h"
 #include "components/password_manager/core/common/password_manager_pref_names.h"
 #include "components/prefs/pref_service.h"
+#include "components/safe_browsing/common/safe_browsing_prefs.h"
 #include "components/translate/core/browser/translate_pref_names.h"
 #include "content/public/browser/notification_service.h"
 #include "content/public/common/webrtc_ip_handling_policy.h"
diff --git a/chrome/browser/media/webrtc/webrtc_browsertest_base.cc b/chrome/browser/media/webrtc/webrtc_browsertest_base.cc
index 1d76576b..4e88d2ace 100644
--- a/chrome/browser/media/webrtc/webrtc_browsertest_base.cc
+++ b/chrome/browser/media/webrtc/webrtc_browsertest_base.cc
@@ -13,6 +13,7 @@
 #include "base/logging.h"
 #include "base/macros.h"
 #include "base/strings/string_number_conversions.h"
+#include "base/strings/string_split.h"
 #include "base/strings/string_util.h"
 #include "base/strings/stringprintf.h"
 #include "base/values.h"
@@ -64,6 +65,7 @@
 const char WebRtcTestBase::kUseDefaultCertKeygen[] = "null";
 const char WebRtcTestBase::kUseDefaultAudioCodec[] = "";
 const char WebRtcTestBase::kUseDefaultVideoCodec[] = "";
+const char WebRtcTestBase::kUndefined[] = "undefined";
 
 namespace {
 
@@ -595,3 +597,97 @@
                           : "verifyRtpReceivers()";
   EXPECT_EQ("ok-receivers-verified", ExecuteJavascript(javascript, tab));
 }
+
+std::vector<std::string> WebRtcTestBase::CreateAndAddAudioAndVideoTrack(
+    content::WebContents* tab,
+    StreamArgumentType stream_argument_type) const {
+  const char* string_argument_type_str = nullptr;
+  switch (stream_argument_type) {
+    case StreamArgumentType::NO_STREAM:
+      string_argument_type_str = "'no-stream'";
+      break;
+    case StreamArgumentType::SHARED_STREAM:
+      string_argument_type_str = "'shared-stream'";
+      break;
+    case StreamArgumentType::INDIVIDUAL_STREAMS:
+      string_argument_type_str = "'individual-streams'";
+      break;
+  }
+  std::string result =
+      ExecuteJavascript(base::StringPrintf("createAndAddAudioAndVideoTrack(%s)",
+                                           string_argument_type_str),
+                        tab);
+  EXPECT_TRUE(base::StartsWith(result, "ok-", base::CompareCase::SENSITIVE));
+  std::vector<std::string> ids = base::SplitString(
+      result.substr(3), " ", base::KEEP_WHITESPACE, base::SPLIT_WANT_ALL);
+  EXPECT_EQ(4u, ids.size());
+  return ids;
+}
+
+void WebRtcTestBase::RemoveTrack(content::WebContents* tab,
+                                 const std::string& track_id) const {
+  EXPECT_EQ(
+      "ok-sender-removed",
+      ExecuteJavascript(
+          base::StringPrintf("removeTrack('%s')", track_id.c_str()), tab));
+}
+
+bool WebRtcTestBase::HasLocalStreamWithTrack(
+    content::WebContents* tab,
+    const std::string& stream_id,
+    const std::string& track_id) const {
+  return HasStreamWithTrack(tab, "hasLocalStreamWithTrack", stream_id,
+                            track_id);
+}
+
+bool WebRtcTestBase::HasRemoteStreamWithTrack(
+    content::WebContents* tab,
+    const std::string& stream_id,
+    const std::string& track_id) const {
+  return HasStreamWithTrack(tab, "hasRemoteStreamWithTrack", stream_id,
+                            track_id);
+}
+
+bool WebRtcTestBase::HasStreamWithTrack(content::WebContents* tab,
+                                        const char* function_name,
+                                        std::string stream_id,
+                                        std::string track_id) const {
+  if (stream_id != kUndefined)
+    stream_id = "'" + stream_id + "'";
+  std::string javascript = base::StringPrintf(
+      "%s(%s, '%s')", function_name, stream_id.c_str(), track_id.c_str());
+  std::string result = ExecuteJavascript(javascript, tab);
+  EXPECT_TRUE(result == "ok-stream-with-track-found" ||
+              result == "ok-stream-with-track-not-found");
+  return result == "ok-stream-with-track-found";
+}
+
+bool WebRtcTestBase::HasSenderWithTrack(content::WebContents* tab,
+                                        std::string track_id) const {
+  std::string javascript =
+      base::StringPrintf("hasSenderWithTrack('%s')", track_id.c_str());
+  std::string result = ExecuteJavascript(javascript, tab);
+  EXPECT_TRUE(result == "ok-sender-with-track-found" ||
+              result == "ok-sender-with-track-not-found");
+  return result == "ok-sender-with-track-found";
+}
+
+bool WebRtcTestBase::HasReceiverWithTrack(content::WebContents* tab,
+                                          std::string track_id) const {
+  std::string javascript =
+      base::StringPrintf("hasReceiverWithTrack('%s')", track_id.c_str());
+  std::string result = ExecuteJavascript(javascript, tab);
+  EXPECT_TRUE(result == "ok-receiver-with-track-found" ||
+              result == "ok-receiver-with-track-not-found");
+  return result == "ok-receiver-with-track-found";
+}
+
+size_t WebRtcTestBase::GetNegotiationNeededCount(
+    content::WebContents* tab) const {
+  std::string result = ExecuteJavascript("getNegotiationNeededCount()", tab);
+  EXPECT_TRUE(base::StartsWith(result, "ok-negotiation-count-is-",
+                               base::CompareCase::SENSITIVE));
+  size_t count = 0;
+  EXPECT_TRUE(base::StringToSizeT(result.substr(24), &count));
+  return count;
+}
diff --git a/chrome/browser/media/webrtc/webrtc_browsertest_base.h b/chrome/browser/media/webrtc/webrtc_browsertest_base.h
index d201fb43..5c16fef 100644
--- a/chrome/browser/media/webrtc/webrtc_browsertest_base.h
+++ b/chrome/browser/media/webrtc/webrtc_browsertest_base.h
@@ -47,6 +47,14 @@
   static const char kUseDefaultAudioCodec[];
   static const char kUseDefaultVideoCodec[];
 
+  static const char kUndefined[];
+
+  enum class StreamArgumentType {
+    NO_STREAM,
+    SHARED_STREAM,
+    INDIVIDUAL_STREAMS
+  };
+
  protected:
   WebRtcTestBase();
   ~WebRtcTestBase() override;
@@ -198,6 +206,22 @@
   void VerifyRtpReceivers(content::WebContents* tab,
                           base::Optional<size_t> expected_num_tracks =
                               base::Optional<size_t>()) const;
+  std::vector<std::string> CreateAndAddAudioAndVideoTrack(
+      content::WebContents* tab,
+      StreamArgumentType stream_argument_type) const;
+  void RemoveTrack(content::WebContents* tab,
+                   const std::string& track_id) const;
+  bool HasLocalStreamWithTrack(content::WebContents* tab,
+                               const std::string& stream_id,
+                               const std::string& track_id) const;
+  bool HasRemoteStreamWithTrack(content::WebContents* tab,
+                                const std::string& stream_id,
+                                const std::string& track_id) const;
+  bool HasSenderWithTrack(content::WebContents* tab,
+                          std::string track_id) const;
+  bool HasReceiverWithTrack(content::WebContents* tab,
+                            std::string track_id) const;
+  size_t GetNegotiationNeededCount(content::WebContents* tab) const;
 
  private:
   void CloseInfoBarInTab(content::WebContents* tab_contents,
@@ -210,6 +234,10 @@
                      content::WebContents* from_tab) const;
   void GatherAndSendIceCandidates(content::WebContents* from_tab,
                                   content::WebContents* to_tab) const;
+  bool HasStreamWithTrack(content::WebContents* tab,
+                          const char* function_name,
+                          std::string stream_id,
+                          std::string track_id) const;
 
   infobars::InfoBar* GetUserMediaAndWaitForInfoBar(
       content::WebContents* tab_contents,
diff --git a/chrome/browser/media/webrtc/webrtc_rtp_browsertest.cc b/chrome/browser/media/webrtc/webrtc_rtp_browsertest.cc
index bd591a9..f935850 100644
--- a/chrome/browser/media/webrtc/webrtc_rtp_browsertest.cc
+++ b/chrome/browser/media/webrtc/webrtc_rtp_browsertest.cc
@@ -2,6 +2,9 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include <string>
+#include <vector>
+
 #include "base/command_line.h"
 #include "chrome/browser/media/webrtc/webrtc_browsertest_base.h"
 #include "content/public/common/content_switches.h"
@@ -63,3 +66,230 @@
   VerifyRtpReceivers(left_tab_, 2);
   VerifyRtpReceivers(right_tab_, 6);
 }
+
+IN_PROC_BROWSER_TEST_F(WebRtcRtpBrowserTest, AddAndRemoveTracksWithoutStream) {
+  StartServerAndOpenTabs();
+
+  SetupPeerconnectionWithoutLocalStream(left_tab_);
+  SetupPeerconnectionWithoutLocalStream(right_tab_);
+
+  // TODO(hbos): Here and in other "AddAndRemoveTracks" tests: when ontrack and
+  // ended events are supported, verify that these are fired on the remote side
+  // when tracks are added and removed. https://crbug.com/webrtc/7933
+
+  // Add two tracks.
+  EXPECT_EQ(0u, GetNegotiationNeededCount(left_tab_));
+  std::vector<std::string> ids =
+      CreateAndAddAudioAndVideoTrack(left_tab_, StreamArgumentType::NO_STREAM);
+  // TODO(hbos): Should only fire once (if the "negotiationneeded" bit changes
+  // from false to true), not once per track added. https://crbug.com/740501
+  EXPECT_EQ(2u, GetNegotiationNeededCount(left_tab_));
+  std::string audio_stream_id = ids[0];
+  std::string audio_track_id = ids[1];
+  std::string video_stream_id = ids[2];
+  std::string video_track_id = ids[3];
+  EXPECT_EQ("null", audio_stream_id);
+  EXPECT_NE("null", audio_track_id);
+  EXPECT_EQ("null", video_stream_id);
+  EXPECT_NE("null", video_track_id);
+  EXPECT_FALSE(
+      HasLocalStreamWithTrack(left_tab_, audio_stream_id, audio_track_id));
+  EXPECT_FALSE(
+      HasLocalStreamWithTrack(left_tab_, video_stream_id, video_track_id));
+  EXPECT_TRUE(HasSenderWithTrack(left_tab_, audio_track_id));
+  EXPECT_TRUE(HasSenderWithTrack(left_tab_, video_track_id));
+  VerifyRtpSenders(left_tab_, 2);
+  // Negotiate call, sets remote description.
+  NegotiateCall(left_tab_, right_tab_);
+  // TODO(hbos): When |addTrack| without streams results in SDP that does not
+  // signal a remote stream to be added this should be EXPECT_FALSE.
+  // https://crbug.com/webrtc/7933
+  EXPECT_TRUE(HasRemoteStreamWithTrack(right_tab_, kUndefined, audio_track_id));
+  EXPECT_TRUE(HasRemoteStreamWithTrack(right_tab_, kUndefined, video_track_id));
+  EXPECT_TRUE(HasReceiverWithTrack(right_tab_, audio_track_id));
+  EXPECT_TRUE(HasReceiverWithTrack(right_tab_, video_track_id));
+  VerifyRtpReceivers(right_tab_, 2);
+
+  // Remove first track.
+  RemoveTrack(left_tab_, audio_track_id);
+  EXPECT_EQ(3u, GetNegotiationNeededCount(left_tab_));
+  EXPECT_FALSE(HasSenderWithTrack(left_tab_, audio_track_id));
+  EXPECT_TRUE(HasSenderWithTrack(left_tab_, video_track_id));
+  VerifyRtpSenders(left_tab_, 1);
+  // Re-negotiate call, sets remote description again.
+  NegotiateCall(left_tab_, right_tab_);
+  EXPECT_FALSE(
+      HasRemoteStreamWithTrack(right_tab_, kUndefined, audio_track_id));
+  EXPECT_TRUE(HasRemoteStreamWithTrack(right_tab_, kUndefined, video_track_id));
+  EXPECT_FALSE(HasReceiverWithTrack(right_tab_, audio_track_id));
+  EXPECT_TRUE(HasReceiverWithTrack(right_tab_, video_track_id));
+  VerifyRtpReceivers(right_tab_, 1);
+
+  // Remove second track.
+  RemoveTrack(left_tab_, video_track_id);
+  EXPECT_EQ(4u, GetNegotiationNeededCount(left_tab_));
+  EXPECT_FALSE(HasSenderWithTrack(left_tab_, audio_track_id));
+  EXPECT_FALSE(HasSenderWithTrack(left_tab_, video_track_id));
+  VerifyRtpSenders(left_tab_, 0);
+  // Re-negotiate call, sets remote description again.
+  NegotiateCall(left_tab_, right_tab_);
+  EXPECT_FALSE(
+      HasRemoteStreamWithTrack(right_tab_, kUndefined, audio_track_id));
+  EXPECT_FALSE(
+      HasRemoteStreamWithTrack(right_tab_, kUndefined, video_track_id));
+  EXPECT_FALSE(HasReceiverWithTrack(right_tab_, audio_track_id));
+  EXPECT_FALSE(HasReceiverWithTrack(right_tab_, video_track_id));
+  VerifyRtpReceivers(right_tab_, 0);
+}
+
+IN_PROC_BROWSER_TEST_F(WebRtcRtpBrowserTest,
+                       AddAndRemoveTracksWithSharedStream) {
+  StartServerAndOpenTabs();
+
+  SetupPeerconnectionWithoutLocalStream(left_tab_);
+  SetupPeerconnectionWithoutLocalStream(right_tab_);
+
+  // Add two tracks.
+  EXPECT_EQ(0u, GetNegotiationNeededCount(left_tab_));
+  std::vector<std::string> ids = CreateAndAddAudioAndVideoTrack(
+      left_tab_, StreamArgumentType::SHARED_STREAM);
+  // TODO(hbos): Should only fire once (if the "negotiationneeded" bit changes
+  // from false to true), not once per track added. https://crbug.com/740501
+  EXPECT_EQ(2u, GetNegotiationNeededCount(left_tab_));
+  std::string audio_stream_id = ids[0];
+  std::string audio_track_id = ids[1];
+  std::string video_stream_id = ids[2];
+  std::string video_track_id = ids[3];
+  EXPECT_NE("null", audio_stream_id);
+  EXPECT_NE("null", audio_track_id);
+  EXPECT_NE("null", video_stream_id);
+  EXPECT_NE("null", video_track_id);
+  EXPECT_EQ(audio_stream_id, video_stream_id);
+  // TODO(hbos): When |getLocalStreams| is updated to return the streams of all
+  // senders, not just |addStream|-streams, then this will be EXPECT_TRUE.
+  // https://crbug.com/738918
+  EXPECT_FALSE(
+      HasLocalStreamWithTrack(left_tab_, audio_stream_id, audio_track_id));
+  EXPECT_FALSE(
+      HasLocalStreamWithTrack(left_tab_, video_stream_id, video_track_id));
+  EXPECT_TRUE(HasSenderWithTrack(left_tab_, audio_track_id));
+  EXPECT_TRUE(HasSenderWithTrack(left_tab_, video_track_id));
+  VerifyRtpSenders(left_tab_, 2);
+  // Negotiate call, sets remote description.
+  NegotiateCall(left_tab_, right_tab_);
+  EXPECT_TRUE(
+      HasRemoteStreamWithTrack(right_tab_, audio_stream_id, audio_track_id));
+  EXPECT_TRUE(
+      HasRemoteStreamWithTrack(right_tab_, video_stream_id, video_track_id));
+  EXPECT_TRUE(HasReceiverWithTrack(right_tab_, audio_track_id));
+  EXPECT_TRUE(HasReceiverWithTrack(right_tab_, video_track_id));
+  VerifyRtpReceivers(right_tab_, 2);
+
+  // Remove first track.
+  RemoveTrack(left_tab_, audio_track_id);
+  EXPECT_EQ(3u, GetNegotiationNeededCount(left_tab_));
+  EXPECT_FALSE(HasSenderWithTrack(left_tab_, audio_track_id));
+  EXPECT_TRUE(HasSenderWithTrack(left_tab_, video_track_id));
+  VerifyRtpSenders(left_tab_, 1);
+  // Re-negotiate call, sets remote description again.
+  NegotiateCall(left_tab_, right_tab_);
+  EXPECT_FALSE(
+      HasRemoteStreamWithTrack(right_tab_, audio_stream_id, audio_track_id));
+  EXPECT_TRUE(
+      HasRemoteStreamWithTrack(right_tab_, video_stream_id, video_track_id));
+  EXPECT_FALSE(HasReceiverWithTrack(right_tab_, audio_track_id));
+  EXPECT_TRUE(HasReceiverWithTrack(right_tab_, video_track_id));
+  VerifyRtpReceivers(right_tab_, 1);
+
+  // Remove second track.
+  RemoveTrack(left_tab_, video_track_id);
+  EXPECT_EQ(4u, GetNegotiationNeededCount(left_tab_));
+  EXPECT_FALSE(HasSenderWithTrack(left_tab_, audio_track_id));
+  EXPECT_FALSE(HasSenderWithTrack(left_tab_, video_track_id));
+  VerifyRtpSenders(left_tab_, 0);
+  // Re-negotiate call, sets remote description again.
+  NegotiateCall(left_tab_, right_tab_);
+  EXPECT_FALSE(
+      HasRemoteStreamWithTrack(right_tab_, audio_stream_id, audio_track_id));
+  EXPECT_FALSE(
+      HasRemoteStreamWithTrack(right_tab_, video_stream_id, video_track_id));
+  EXPECT_FALSE(HasReceiverWithTrack(right_tab_, audio_track_id));
+  EXPECT_FALSE(HasReceiverWithTrack(right_tab_, video_track_id));
+  VerifyRtpReceivers(right_tab_, 0);
+}
+
+IN_PROC_BROWSER_TEST_F(WebRtcRtpBrowserTest,
+                       AddAndRemoveTracksWithIndividualStreams) {
+  StartServerAndOpenTabs();
+
+  SetupPeerconnectionWithoutLocalStream(left_tab_);
+  SetupPeerconnectionWithoutLocalStream(right_tab_);
+
+  // Add two tracks.
+  EXPECT_EQ(0u, GetNegotiationNeededCount(left_tab_));
+  std::vector<std::string> ids = CreateAndAddAudioAndVideoTrack(
+      left_tab_, StreamArgumentType::INDIVIDUAL_STREAMS);
+  // TODO(hbos): Should only fire once (if the "negotiationneeded" bit changes
+  // from false to true), not once per track added. https://crbug.com/740501
+  EXPECT_EQ(2u, GetNegotiationNeededCount(left_tab_));
+  std::string audio_stream_id = ids[0];
+  std::string audio_track_id = ids[1];
+  std::string video_stream_id = ids[2];
+  std::string video_track_id = ids[3];
+  EXPECT_NE("null", audio_stream_id);
+  EXPECT_NE("null", audio_track_id);
+  EXPECT_NE("null", video_stream_id);
+  EXPECT_NE("null", video_track_id);
+  EXPECT_NE(audio_stream_id, video_stream_id);
+  // TODO(hbos): When |getLocalStreams| is updated to return the streams of all
+  // senders, not just |addStream|-streams, then this will be EXPECT_TRUE.
+  // https://crbug.com/738918
+  EXPECT_FALSE(
+      HasLocalStreamWithTrack(left_tab_, audio_stream_id, audio_track_id));
+  EXPECT_FALSE(
+      HasLocalStreamWithTrack(left_tab_, video_stream_id, video_track_id));
+  EXPECT_TRUE(HasSenderWithTrack(left_tab_, audio_track_id));
+  EXPECT_TRUE(HasSenderWithTrack(left_tab_, video_track_id));
+  VerifyRtpSenders(left_tab_, 2);
+  // Negotiate call, sets remote description.
+  NegotiateCall(left_tab_, right_tab_);
+  EXPECT_TRUE(
+      HasRemoteStreamWithTrack(right_tab_, audio_stream_id, audio_track_id));
+  EXPECT_TRUE(
+      HasRemoteStreamWithTrack(right_tab_, video_stream_id, video_track_id));
+  EXPECT_TRUE(HasReceiverWithTrack(right_tab_, audio_track_id));
+  EXPECT_TRUE(HasReceiverWithTrack(right_tab_, video_track_id));
+  VerifyRtpReceivers(right_tab_, 2);
+
+  // Remove first track.
+  RemoveTrack(left_tab_, audio_track_id);
+  EXPECT_EQ(3u, GetNegotiationNeededCount(left_tab_));
+  EXPECT_FALSE(HasSenderWithTrack(left_tab_, audio_track_id));
+  EXPECT_TRUE(HasSenderWithTrack(left_tab_, video_track_id));
+  VerifyRtpSenders(left_tab_, 1);
+  // Re-negotiate call, sets remote description again.
+  NegotiateCall(left_tab_, right_tab_);
+  EXPECT_FALSE(
+      HasRemoteStreamWithTrack(right_tab_, audio_stream_id, audio_track_id));
+  EXPECT_TRUE(
+      HasRemoteStreamWithTrack(right_tab_, video_stream_id, video_track_id));
+  EXPECT_FALSE(HasReceiverWithTrack(right_tab_, audio_track_id));
+  EXPECT_TRUE(HasReceiverWithTrack(right_tab_, video_track_id));
+  VerifyRtpReceivers(right_tab_, 1);
+
+  // Remove second track.
+  RemoveTrack(left_tab_, video_track_id);
+  EXPECT_EQ(4u, GetNegotiationNeededCount(left_tab_));
+  EXPECT_FALSE(HasSenderWithTrack(left_tab_, audio_track_id));
+  EXPECT_FALSE(HasSenderWithTrack(left_tab_, video_track_id));
+  VerifyRtpSenders(left_tab_, 0);
+  // Re-negotiate call, sets remote description again.
+  NegotiateCall(left_tab_, right_tab_);
+  EXPECT_FALSE(
+      HasRemoteStreamWithTrack(right_tab_, audio_stream_id, audio_track_id));
+  EXPECT_FALSE(
+      HasRemoteStreamWithTrack(right_tab_, video_stream_id, video_track_id));
+  EXPECT_FALSE(HasReceiverWithTrack(right_tab_, audio_track_id));
+  EXPECT_FALSE(HasReceiverWithTrack(right_tab_, video_track_id));
+  VerifyRtpReceivers(right_tab_, 0);
+}
diff --git a/chrome/browser/ntp_tiles/ntp_tiles_browsertest.cc b/chrome/browser/ntp_tiles/ntp_tiles_browsertest.cc
new file mode 100644
index 0000000..2d385ad
--- /dev/null
+++ b/chrome/browser/ntp_tiles/ntp_tiles_browsertest.cc
@@ -0,0 +1,105 @@
+// 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 "base/strings/utf_string_conversions.h"
+#include "chrome/browser/ntp_tiles/chrome_most_visited_sites_factory.h"
+#include "chrome/browser/ui/browser.h"
+#include "chrome/test/base/in_process_browser_test.h"
+#include "chrome/test/base/ui_test_utils.h"
+#include "components/ntp_tiles/most_visited_sites.h"
+#include "components/ntp_tiles/ntp_tile.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "url/gurl.h"
+
+namespace ntp_tiles {
+
+namespace {
+
+using testing::Contains;
+
+std::string PrintTile(const std::string& title,
+                      const std::string& url,
+                      TileSource source) {
+  return std::string("has title \"") + title + std::string("\" and url \"") +
+         url + std::string("\" and source ") +
+         testing::PrintToString(static_cast<int>(source));
+}
+
+MATCHER_P3(MatchesTile, title, url, source, PrintTile(title, url, source)) {
+  return arg.title == base::ASCIIToUTF16(title) && arg.url == GURL(url) &&
+         arg.source == source;
+}
+
+// Waits for most visited URLs to be made available.
+class MostVisitedSitesWaiter : public MostVisitedSites::Observer {
+ public:
+  MostVisitedSitesWaiter() : tiles_(NTPTilesVector()) {}
+
+  // Waits until most visited URLs are available, and then returns all the
+  // tiles.
+  NTPTilesVector WaitForTiles() {
+    base::RunLoop run_loop;
+    quit_closure_ = run_loop.QuitClosure();
+    run_loop.Run();
+    return tiles_;
+  }
+
+  void OnMostVisitedURLsAvailable(const NTPTilesVector& tiles) override {
+    tiles_ = tiles;
+    if (!quit_closure_.is_null()) {
+      quit_closure_.Run();
+    }
+  }
+
+  void OnIconMadeAvailable(const GURL& site_url) override {}
+
+ private:
+  base::Closure quit_closure_;
+  NTPTilesVector tiles_;
+};
+
+}  // namespace
+
+class NTPTilesTest : public InProcessBrowserTest {
+ public:
+  NTPTilesTest() {}
+
+ protected:
+  void SetUpOnMainThread() override {
+    most_visited_sites_ =
+        ChromeMostVisitedSitesFactory::NewForProfile(browser()->profile());
+  }
+
+  void TearDownOnMainThread() override {
+    // Reset most_visited_sites_, otherwise there is a CHECK in callback_list.h
+    // because callbacks_.size() is not 0.
+    most_visited_sites_.reset();
+  }
+
+  std::unique_ptr<ntp_tiles::MostVisitedSites> most_visited_sites_;
+};
+
+// Tests that after navigating to a URL, ntp tiles will include the URL.
+IN_PROC_BROWSER_TEST_F(NTPTilesTest, LoadURL) {
+  ASSERT_TRUE(embedded_test_server()->Start());
+  const GURL page_url = embedded_test_server()->GetURL("/simple.html");
+
+  ui_test_utils::NavigateToURLWithDisposition(
+      browser(), page_url, WindowOpenDisposition::CURRENT_TAB,
+      ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION);
+
+  MostVisitedSitesWaiter waiter;
+
+  // This call will call SyncWithHistory(), which means the new URL will be in
+  // the next set of tiles that the waiter retrieves.
+  most_visited_sites_->SetMostVisitedURLsObserver(&waiter, /*num_sites=*/8);
+
+  NTPTilesVector tiles = waiter.WaitForTiles();
+  EXPECT_THAT(tiles, Contains(MatchesTile("OK", page_url.spec().c_str(),
+                                          TileSource::TOP_SITES)));
+}
+
+}  // namespace ntp_tiles
diff --git a/chrome/browser/permissions/permission_uma_util_unittest.cc b/chrome/browser/permissions/permission_uma_util_unittest.cc
index 55bc0dd..96960bc 100644
--- a/chrome/browser/permissions/permission_uma_util_unittest.cc
+++ b/chrome/browser/permissions/permission_uma_util_unittest.cc
@@ -18,6 +18,7 @@
 #include "components/browser_sync/profile_sync_service.h"
 #include "components/browser_sync/profile_sync_service_mock.h"
 #include "components/prefs/pref_service.h"
+#include "components/safe_browsing/common/safe_browsing_prefs.h"
 #include "components/sync/base/model_type.h"
 #include "components/sync/base/sync_prefs.h"
 #include "components/sync/engine/fake_sync_engine.h"
diff --git a/chrome/browser/prefs/chrome_pref_service_factory.cc b/chrome/browser/prefs/chrome_pref_service_factory.cc
index 3211483..5344edf5 100644
--- a/chrome/browser/prefs/chrome_pref_service_factory.cc
+++ b/chrome/browser/prefs/chrome_pref_service_factory.cc
@@ -47,6 +47,7 @@
 #include "components/prefs/pref_service.h"
 #include "components/prefs/pref_store.h"
 #include "components/prefs/pref_value_store.h"
+#include "components/safe_browsing/common/safe_browsing_prefs.h"
 #include "components/search_engines/default_search_manager.h"
 #include "components/search_engines/search_engines_pref_names.h"
 #include "components/signin/core/common/signin_pref_names.h"
diff --git a/chrome/browser/prefs/pref_functional_browsertest.cc b/chrome/browser/prefs/pref_functional_browsertest.cc
index ec142db..9d671e3 100644
--- a/chrome/browser/prefs/pref_functional_browsertest.cc
+++ b/chrome/browser/prefs/pref_functional_browsertest.cc
@@ -23,6 +23,7 @@
 #include "components/content_settings/core/browser/website_settings_registry.h"
 #include "components/content_settings/core/common/content_settings_types.h"
 #include "components/content_settings/core/common/pref_names.h"
+#include "components/safe_browsing/common/safe_browsing_prefs.h"
 #include "components/sync_preferences/pref_service_syncable.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/test/browser_test_utils.h"
diff --git a/chrome/browser/profiles/off_the_record_profile_io_data.cc b/chrome/browser/profiles/off_the_record_profile_io_data.cc
index f04aef7..d08c5fe 100644
--- a/chrome/browser/profiles/off_the_record_profile_io_data.cc
+++ b/chrome/browser/profiles/off_the_record_profile_io_data.cc
@@ -21,10 +21,10 @@
 #include "chrome/browser/net/chrome_url_request_context_getter.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/common/chrome_switches.h"
-#include "chrome/common/pref_names.h"
 #include "chrome/common/url_constants.h"
 #include "components/net_log/chrome_net_log.h"
 #include "components/prefs/pref_service.h"
+#include "components/safe_browsing/common/safe_browsing_prefs.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/cookie_store_factory.h"
 #include "content/public/browser/resource_context.h"
diff --git a/chrome/browser/profiles/profile.cc b/chrome/browser/profiles/profile.cc
index 0c2eb4a1..9fe01f76 100644
--- a/chrome/browser/profiles/profile.cc
+++ b/chrome/browser/profiles/profile.cc
@@ -143,14 +143,7 @@
   registry->RegisterStringPref(prefs::kSessionExitType, std::string());
   registry->RegisterInt64Pref(prefs::kSiteEngagementLastUpdateTime, 0,
                               PrefRegistry::LOSSY_PREF);
-  registry->RegisterBooleanPref(
-      prefs::kSafeBrowsingEnabled,
-      true,
-      user_prefs::PrefRegistrySyncable::SYNCABLE_PREF);
-  registry->RegisterBooleanPref(prefs::kSafeBrowsingProceedAnywayDisabled,
-                                false);
   registry->RegisterBooleanPref(prefs::kSSLErrorOverrideAllowed, true);
-  registry->RegisterDictionaryPref(prefs::kSafeBrowsingIncidentsSent);
   // This pref is intentionally outside the above #if. That flag corresponds
   // to the Notifier extension and does not gate the launcher page.
   // TODO(skare): Remove or rename ENABLE_GOOGLE_NOW: http://crbug.com/459827.
diff --git a/chrome/browser/profiles/profile_impl_io_data.cc b/chrome/browser/profiles/profile_impl_io_data.cc
index f35c5a4..e308fd6 100644
--- a/chrome/browser/profiles/profile_impl_io_data.cc
+++ b/chrome/browser/profiles/profile_impl_io_data.cc
@@ -38,7 +38,6 @@
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/common/chrome_constants.h"
 #include "chrome/common/chrome_switches.h"
-#include "chrome/common/pref_names.h"
 #include "chrome/common/url_constants.h"
 #include "components/cookie_config/cookie_store_util.h"
 #include "components/data_reduction_proxy/core/browser/data_reduction_proxy_io_data.h"
@@ -51,6 +50,7 @@
 #include "components/prefs/pref_member.h"
 #include "components/prefs/pref_service.h"
 #include "components/previews/core/previews_io_data.h"
+#include "components/safe_browsing/common/safe_browsing_prefs.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/cookie_store_factory.h"
 #include "content/public/browser/notification_service.h"
diff --git a/chrome/browser/safe_browsing/BUILD.gn b/chrome/browser/safe_browsing/BUILD.gn
index e2aa8bf..457a58a1 100644
--- a/chrome/browser/safe_browsing/BUILD.gn
+++ b/chrome/browser/safe_browsing/BUILD.gn
@@ -231,7 +231,10 @@
         "v4_test_utils.cc",
         "v4_test_utils.h",
       ]
-      deps += [ "//components/safe_browsing_db:safe_browsing_db" ]
+      deps += [
+        "//components/safe_browsing/common:safe_browsing_prefs",
+        "//components/safe_browsing_db:safe_browsing_db",
+      ]
       if (is_win) {
         deps += [ "//chrome/browser/safe_browsing/incident_reporting:state_store_data_proto" ]
       }
diff --git a/chrome/browser/safe_browsing/certificate_reporting_service_browsertest.cc b/chrome/browser/safe_browsing/certificate_reporting_service_browsertest.cc
index 297ec1c..468c1e69 100644
--- a/chrome/browser/safe_browsing/certificate_reporting_service_browsertest.cc
+++ b/chrome/browser/safe_browsing/certificate_reporting_service_browsertest.cc
@@ -20,11 +20,11 @@
 #include "chrome/browser/ssl/certificate_reporting_test_utils.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
-#include "chrome/common/pref_names.h"
 #include "chrome/test/base/in_process_browser_test.h"
 #include "chrome/test/base/ui_test_utils.h"
 #include "components/certificate_reporting/error_report.h"
 #include "components/prefs/pref_service.h"
+#include "components/safe_browsing/common/safe_browsing_prefs.h"
 #include "components/variations/variations_switches.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/test/browser_test_utils.h"
diff --git a/chrome/browser/safe_browsing/incident_reporting/state_store.cc b/chrome/browser/safe_browsing/incident_reporting/state_store.cc
index eb3a5a6..fc9b6357 100644
--- a/chrome/browser/safe_browsing/incident_reporting/state_store.cc
+++ b/chrome/browser/safe_browsing/incident_reporting/state_store.cc
@@ -13,8 +13,8 @@
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/safe_browsing/incident_reporting/incident.h"
 #include "chrome/browser/safe_browsing/incident_reporting/platform_state_store.h"
-#include "chrome/common/pref_names.h"
 #include "components/prefs/pref_service.h"
+#include "components/safe_browsing/common/safe_browsing_prefs.h"
 
 namespace safe_browsing {
 
diff --git a/chrome/browser/safe_browsing/incident_reporting/state_store_unittest.cc b/chrome/browser/safe_browsing/incident_reporting/state_store_unittest.cc
index 95d7b93f..c924198 100644
--- a/chrome/browser/safe_browsing/incident_reporting/state_store_unittest.cc
+++ b/chrome/browser/safe_browsing/incident_reporting/state_store_unittest.cc
@@ -19,11 +19,11 @@
 #include "chrome/browser/prefs/browser_prefs.h"
 #include "chrome/browser/safe_browsing/incident_reporting/incident.h"
 #include "chrome/browser/safe_browsing/incident_reporting/platform_state_store.h"
-#include "chrome/common/pref_names.h"
 #include "chrome/test/base/testing_browser_process.h"
 #include "chrome/test/base/testing_profile.h"
 #include "chrome/test/base/testing_profile_manager.h"
 #include "components/pref_registry/pref_registry_syncable.h"
+#include "components/safe_browsing/common/safe_browsing_prefs.h"
 #include "components/sync_preferences/pref_service_syncable.h"
 #include "components/sync_preferences/pref_service_syncable_factory.h"
 #include "content/public/test/test_browser_thread_bundle.h"
diff --git a/chrome/browser/safe_browsing/settings_reset_prompt/settings_reset_dependency_browsertest_win.cc b/chrome/browser/safe_browsing/settings_reset_prompt/settings_reset_dependency_browsertest_win.cc
new file mode 100644
index 0000000..3fd58dd
--- /dev/null
+++ b/chrome/browser/safe_browsing/settings_reset_prompt/settings_reset_dependency_browsertest_win.cc
@@ -0,0 +1,92 @@
+// 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 <memory>
+#include <tuple>
+
+#include "base/strings/string_util.h"
+#include "base/test/scoped_feature_list.h"
+#include "chrome/browser/safe_browsing/chrome_cleaner/settings_resetter_win.h"
+#include "chrome/browser/safe_browsing/chrome_cleaner/srt_field_trial_win.h"
+#include "chrome/browser/safe_browsing/settings_reset_prompt/settings_reset_prompt_controller.h"
+#include "chrome/browser/safe_browsing/settings_reset_prompt/settings_reset_prompt_test_utils.h"
+#include "chrome/test/base/in_process_browser_test.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace safe_browsing {
+namespace {
+
+using ::testing::InvokeWithoutArgs;
+using ::testing::StrictMock;
+
+class MockSettingsResetPromptDelegate : public SettingsResetPromptDelegate {
+ public:
+  MOCK_CONST_METHOD0(ShowSettingsResetPromptWithDelay, void());
+};
+
+// Test params: in_browser_cleaner_ui_enabled, settings_reset_prompt_enabled,
+//              representing if each feature is enabled.
+class SettingsResetDependencyTest
+    : public InProcessBrowserTest,
+      public ::testing::WithParamInterface<std::tuple<bool, bool>> {
+ public:
+  void SetUpInProcessBrowserTestFixture() override {
+    SetSettingsResetPromptDelegate(&delegate_);
+
+    std::tie(in_browser_cleaner_ui_enabled_, settings_reset_prompt_enabled_) =
+        GetParam();
+
+    std::vector<base::StringPiece> enabled_features;
+    std::vector<base::StringPiece> disabled_features;
+    if (in_browser_cleaner_ui_enabled_) {
+      enabled_features.push_back(kInBrowserCleanerUIFeature.name);
+    } else {
+      disabled_features.push_back(kInBrowserCleanerUIFeature.name);
+    }
+
+    if (settings_reset_prompt_enabled_) {
+      enabled_features.push_back(kSettingsResetPrompt.name);
+      EXPECT_CALL(delegate_, ShowSettingsResetPromptWithDelay())
+          .WillOnce(
+              InvokeWithoutArgs([this] { reset_prompt_checked_ = true; }));
+    } else {
+      disabled_features.push_back(kSettingsResetPrompt.name);
+    }
+
+    scoped_feature_list_.InitFromCommandLine(
+        base::JoinString(enabled_features, ","),
+        base::JoinString(disabled_features, ","));
+  }
+
+  void TearDownInProcessBrowserTestFixture() override {
+    SetSettingsResetPromptDelegate(nullptr);
+  }
+
+ protected:
+  bool in_browser_cleaner_ui_enabled_;
+  bool settings_reset_prompt_enabled_;
+
+  bool reset_prompt_checked_ = false;
+
+  StrictMock<MockSettingsResetPromptDelegate> delegate_;
+
+  base::test::ScopedFeatureList scoped_feature_list_;
+};
+
+IN_PROC_BROWSER_TEST_P(SettingsResetDependencyTest,
+                       PromptAfterPostCleanupReset) {
+  if (settings_reset_prompt_enabled_) {
+    while (!reset_prompt_checked_)
+      base::RunLoop().RunUntilIdle();
+  }
+}
+
+INSTANTIATE_TEST_CASE_P(Default,
+                        SettingsResetDependencyTest,
+                        ::testing::Combine(::testing::Bool(),
+                                           ::testing::Bool()));
+
+}  // namespace
+}  // namespace safe_browsing
diff --git a/chrome/browser/safe_browsing/settings_reset_prompt/settings_reset_prompt_controller.cc b/chrome/browser/safe_browsing/settings_reset_prompt/settings_reset_prompt_controller.cc
index 0d79644..b8e9169c 100644
--- a/chrome/browser/safe_browsing/settings_reset_prompt/settings_reset_prompt_controller.cc
+++ b/chrome/browser/safe_browsing/settings_reset_prompt/settings_reset_prompt_controller.cc
@@ -107,6 +107,36 @@
       base::Bind(&TryToShowSettingsResetPrompt, base::Passed(&model)));
 }
 
+class SettingsResetPromptDelegateImpl : public SettingsResetPromptDelegate {
+ public:
+  SettingsResetPromptDelegateImpl();
+  ~SettingsResetPromptDelegateImpl() override;
+
+  void ShowSettingsResetPromptWithDelay() const override;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(SettingsResetPromptDelegateImpl);
+};
+
+SettingsResetPromptDelegateImpl::SettingsResetPromptDelegateImpl() = default;
+
+SettingsResetPromptDelegateImpl::~SettingsResetPromptDelegateImpl() = default;
+
+void SettingsResetPromptDelegateImpl::ShowSettingsResetPromptWithDelay() const {
+  std::unique_ptr<SettingsResetPromptConfig> config =
+      SettingsResetPromptConfig::Create();
+  if (!config)
+    return;
+
+  base::TimeDelta delay = config->delay_before_prompt();
+  content::BrowserThread::PostDelayedTask(
+      content::BrowserThread::UI, FROM_HERE,
+      base::BindOnce(MaybeShowSettingsResetPrompt, base::Passed(&config)),
+      delay);
+}
+
+SettingsResetPromptDelegate* g_settings_reset_prompt_delegate = nullptr;
+
 }  // namespace.
 
 SettingsResetPromptController::SettingsResetPromptController(
@@ -257,16 +287,19 @@
 }
 
 void MaybeShowSettingsResetPromptWithDelay() {
-  std::unique_ptr<SettingsResetPromptConfig> config =
-      SettingsResetPromptConfig::Create();
-  if (!config)
-    return;
+  if (g_settings_reset_prompt_delegate) {
+    g_settings_reset_prompt_delegate->ShowSettingsResetPromptWithDelay();
+  } else {
+    SettingsResetPromptDelegateImpl().ShowSettingsResetPromptWithDelay();
+  }
+}
 
-  base::TimeDelta delay = config->delay_before_prompt();
-  content::BrowserThread::PostDelayedTask(
-      content::BrowserThread::UI, FROM_HERE,
-      base::BindOnce(MaybeShowSettingsResetPrompt, base::Passed(&config)),
-      delay);
+SettingsResetPromptDelegate::SettingsResetPromptDelegate() = default;
+
+SettingsResetPromptDelegate::~SettingsResetPromptDelegate() = default;
+
+void SetSettingsResetPromptDelegate(SettingsResetPromptDelegate* delegate) {
+  g_settings_reset_prompt_delegate = delegate;
 }
 
 }  // namespace safe_browsing
diff --git a/chrome/browser/safe_browsing/settings_reset_prompt/settings_reset_prompt_controller.h b/chrome/browser/safe_browsing/settings_reset_prompt/settings_reset_prompt_controller.h
index a277bd0..9496c1da 100644
--- a/chrome/browser/safe_browsing/settings_reset_prompt/settings_reset_prompt_controller.h
+++ b/chrome/browser/safe_browsing/settings_reset_prompt/settings_reset_prompt_controller.h
@@ -83,6 +83,23 @@
 // feature parameters.
 void MaybeShowSettingsResetPromptWithDelay();
 
+// Delegate for MaybeShowSettingsResetPromptWithDelay() that can be overriden
+// by tests that only want to check if the flow for the settings reset prompt
+// will be initiated.
+class SettingsResetPromptDelegate {
+ public:
+  SettingsResetPromptDelegate();
+  virtual ~SettingsResetPromptDelegate();
+
+  virtual void ShowSettingsResetPromptWithDelay() const = 0;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(SettingsResetPromptDelegate);
+};
+
+// Sets the global SettingsResetPromptDelegate, usually for testing.
+void SetSettingsResetPromptDelegate(SettingsResetPromptDelegate* delegate);
+
 }  // namespace safe_browsing
 
 #endif  // CHROME_BROWSER_SAFE_BROWSING_SETTINGS_RESET_PROMPT_SETTINGS_RESET_PROMPT_CONTROLLER_H_
diff --git a/chrome/browser/sync/test/integration/single_client_bookmarks_sync_test.cc b/chrome/browser/sync/test/integration/single_client_bookmarks_sync_test.cc
index 746f213e..751b14b 100644
--- a/chrome/browser/sync/test/integration/single_client_bookmarks_sync_test.cc
+++ b/chrome/browser/sync/test/integration/single_client_bookmarks_sync_test.cc
@@ -16,7 +16,6 @@
 #include "components/sync/test/fake_server/bookmark_entity_builder.h"
 #include "components/sync/test/fake_server/entity_builder_factory.h"
 #include "components/sync/test/fake_server/fake_server_verifier.h"
-#include "components/sync/test/fake_server/tombstone_entity.h"
 #include "ui/base/layout.h"
 
 using bookmarks::BookmarkModel;
@@ -347,8 +346,8 @@
       GetFakeServer()->GetSyncEntitiesByModelType(syncer::BOOKMARKS);
   ASSERT_EQ(1ul, server_bookmarks.size());
   std::string entity_id = server_bookmarks[0].id_string();
-  std::unique_ptr<fake_server::FakeServerEntity> tombstone(
-      fake_server::TombstoneEntity::Create(entity_id, std::string()));
+  std::unique_ptr<syncer::LoopbackServerEntity> tombstone(
+      syncer::PersistentTombstoneEntity::CreateNew(entity_id, std::string()));
   GetFakeServer()->InjectEntity(std::move(tombstone));
 
   const syncer::ModelTypeSet kBookmarksType(syncer::BOOKMARKS);
diff --git a/chrome/browser/sync/test/integration/single_client_extensions_sync_test.cc b/chrome/browser/sync/test/integration/single_client_extensions_sync_test.cc
index 1145f21..cda11f3 100644
--- a/chrome/browser/sync/test/integration/single_client_extensions_sync_test.cc
+++ b/chrome/browser/sync/test/integration/single_client_extensions_sync_test.cc
@@ -8,7 +8,7 @@
 #include "chrome/browser/sync/test/integration/sync_test.h"
 #include "chrome/browser/sync/test/integration/updated_progress_marker_checker.h"
 #include "components/browser_sync/profile_sync_service.h"
-#include "components/sync/test/fake_server/tombstone_entity.h"
+#include "components/sync/test/fake_server/fake_server.h"
 
 using extensions_helper::AllProfilesHaveSameExtensionsAsVerifier;
 using extensions_helper::DisableExtension;
@@ -78,8 +78,8 @@
   std::vector<sync_pb::SyncEntity> server_extensions =
       GetFakeServer()->GetSyncEntitiesByModelType(syncer::EXTENSIONS);
   ASSERT_EQ(1ul, server_extensions.size());
-  std::unique_ptr<fake_server::FakeServerEntity> tombstone(
-      fake_server::TombstoneEntity::Create(
+  std::unique_ptr<syncer::LoopbackServerEntity> tombstone(
+      syncer::PersistentTombstoneEntity::CreateNew(
           server_extensions[0].id_string(),
           server_extensions[0].client_defined_unique_tag()));
   GetFakeServer()->InjectEntity(std::move(tombstone));
diff --git a/chrome/browser/sync/test/integration/single_client_user_events_sync_test.cc b/chrome/browser/sync/test/integration/single_client_user_events_sync_test.cc
index d5f6929..809f704 100644
--- a/chrome/browser/sync/test/integration/single_client_user_events_sync_test.cc
+++ b/chrome/browser/sync/test/integration/single_client_user_events_sync_test.cc
@@ -30,14 +30,14 @@
 
 CommitResponse::ResponseType BounceType(
     CommitResponse::ResponseType type,
-    const fake_server::FakeServerEntity& entity) {
+    const syncer::LoopbackServerEntity& entity) {
   return type;
 }
 
 CommitResponse::ResponseType TransientErrorFirst(
     bool* first,
     UserEventSpecifics* retry_specifics,
-    const fake_server::FakeServerEntity& entity) {
+    const syncer::LoopbackServerEntity& entity) {
   if (*first) {
     *first = false;
     SyncEntity sync_entity;
diff --git a/chrome/browser/sync/test/integration/single_client_wallet_sync_test.cc b/chrome/browser/sync/test/integration/single_client_wallet_sync_test.cc
index 80e9b2c5..00c8205 100644
--- a/chrome/browser/sync/test/integration/single_client_wallet_sync_test.cc
+++ b/chrome/browser/sync/test/integration/single_client_wallet_sync_test.cc
@@ -19,8 +19,7 @@
 #include "components/browser_sync/profile_sync_service.h"
 #include "components/prefs/pref_service.h"
 #include "components/sync/base/model_type.h"
-#include "components/sync/test/fake_server/fake_server_entity.h"
-#include "components/sync/test/fake_server/unique_client_entity.h"
+#include "components/sync/test/fake_server/fake_server.h"
 #include "content/public/browser/notification_service.h"
 
 using autofill_helper::GetPersonalDataManager;
@@ -52,9 +51,9 @@
   credit_card->set_status(sync_pb::WalletMaskedCreditCard::VALID);
   credit_card->set_type(kDefaultCardType);
 
-  server->InjectEntity(fake_server::UniqueClientEntity::CreateForInjection(
-      kDefaultCardID,
-      specifics));
+  server->InjectEntity(
+      syncer::PersistentUniqueClientEntity::CreateFromEntitySpecifics(
+          kDefaultCardID, specifics));
 }
 
 }  // namespace
diff --git a/chrome/browser/ui/webui/md_downloads/md_downloads_dom_handler.cc b/chrome/browser/ui/webui/md_downloads/md_downloads_dom_handler.cc
index 15bbabc..fdc3fbe2 100644
--- a/chrome/browser/ui/webui/md_downloads/md_downloads_dom_handler.cc
+++ b/chrome/browser/ui/webui/md_downloads/md_downloads_dom_handler.cc
@@ -33,6 +33,7 @@
 #include "chrome/common/pref_names.h"
 #include "chrome/common/url_constants.h"
 #include "components/prefs/pref_service.h"
+#include "components/safe_browsing/common/safe_browsing_prefs.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/download_danger_type.h"
 #include "content/public/browser/download_item.h"
diff --git a/chrome/common/BUILD.gn b/chrome/common/BUILD.gn
index 292f4761..aae37029 100644
--- a/chrome/common/BUILD.gn
+++ b/chrome/common/BUILD.gn
@@ -233,7 +233,6 @@
     "//components/ntp_tiles",
     "//components/offline_pages/core:switches",
     "//components/omnibox/common",
-    "//components/password_manager/content/common:mojo_interfaces",
     "//components/password_manager/core/common",
     "//components/policy:generated",
     "//components/policy/core/common",
diff --git a/chrome/common/pref_names.cc b/chrome/common/pref_names.cc
index cc393e19..07e766f 100644
--- a/chrome/common/pref_names.cc
+++ b/chrome/common/pref_names.cc
@@ -421,17 +421,6 @@
 // (crbug.com/564207).
 const char kDataSaverEnabled[] = "spdy_proxy.enabled";
 
-// Boolean that is true when SafeBrowsing is enabled.
-const char kSafeBrowsingEnabled[] = "safebrowsing.enabled";
-
-// Boolean that is true when the SafeBrowsing interstitial should not allow
-// users to proceed anyway.
-const char kSafeBrowsingProceedAnywayDisabled[] =
-    "safebrowsing.proceed_anyway_disabled";
-
-// A dictionary mapping incident types to a dict of incident key:digest pairs.
-const char kSafeBrowsingIncidentsSent[] = "safebrowsing.incidents_sent";
-
 // Boolean that is true when the SSL interstitial should allow users to
 // proceed anyway. Otherwise, proceeding is not possible.
 const char kSSLErrorOverrideAllowed[] = "ssl.error_override_allowed";
diff --git a/chrome/common/pref_names.h b/chrome/common/pref_names.h
index 90a87ec..16dc991 100644
--- a/chrome/common/pref_names.h
+++ b/chrome/common/pref_names.h
@@ -173,9 +173,6 @@
 extern const char kWebKitPasswordEchoEnabled[];
 #endif
 extern const char kDataSaverEnabled[];
-extern const char kSafeBrowsingEnabled[];
-extern const char kSafeBrowsingProceedAnywayDisabled[];
-extern const char kSafeBrowsingIncidentsSent[];
 extern const char kSSLErrorOverrideAllowed[];
 extern const char kIncognitoModeAvailability[];
 extern const char kSearchSuggestEnabled[];
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index 3a190b1a..388a01c 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -1357,6 +1357,7 @@
       "../browser/net/sdch_browsertest.cc",
       "../browser/net/websocket_browsertest.cc",
       "../browser/ntp_snippets/content_suggestions_service_factory_browsertest.cc",
+      "../browser/ntp_tiles/ntp_tiles_browsertest.cc",
       "../browser/page_load_metrics/observers/ads_page_load_metrics_observer_browsertest.cc",
       "../browser/page_load_metrics/observers/https_engagement_metrics/https_engagement_page_load_metrics_observer_browsertest.cc",
       "../browser/page_load_metrics/observers/multi_tab_loading_page_load_metrics_observer_browsertest.cc",
@@ -2000,6 +2001,7 @@
         "../browser/extensions/window_open_apitest.cc",
         "../browser/safe_browsing/chrome_cleaner/settings_resetter_browsertest_win.cc",
         "../browser/safe_browsing/settings_reset_prompt/default_settings_fetcher_browsertest.cc",
+        "../browser/safe_browsing/settings_reset_prompt/settings_reset_dependency_browsertest_win.cc",
         "../browser/safe_browsing/settings_reset_prompt/settings_reset_prompt_model_browsertest_win.cc",
         "../browser/safe_browsing/settings_reset_prompt/settings_reset_prompt_test_utils.cc",
         "../browser/safe_browsing/settings_reset_prompt/settings_reset_prompt_test_utils.h",
diff --git a/chrome/test/data/android/UiCapture/conductor.jpg b/chrome/test/data/android/UiCapture/conductor.jpg
new file mode 100644
index 0000000..7534042
--- /dev/null
+++ b/chrome/test/data/android/UiCapture/conductor.jpg
Binary files differ
diff --git a/chrome/test/data/android/UiCapture/fire.jpg b/chrome/test/data/android/UiCapture/fire.jpg
new file mode 100644
index 0000000..1fd0a0c
--- /dev/null
+++ b/chrome/test/data/android/UiCapture/fire.jpg
Binary files differ
diff --git a/chrome/test/data/android/UiCapture/gig.jpg b/chrome/test/data/android/UiCapture/gig.jpg
new file mode 100644
index 0000000..e8456fc3
--- /dev/null
+++ b/chrome/test/data/android/UiCapture/gig.jpg
Binary files differ
diff --git a/chrome/test/data/android/UiCapture/goat.jpg b/chrome/test/data/android/UiCapture/goat.jpg
new file mode 100644
index 0000000..2133c4a
--- /dev/null
+++ b/chrome/test/data/android/UiCapture/goat.jpg
Binary files differ
diff --git a/chrome/test/data/webrtc/munge_sdp.js b/chrome/test/data/webrtc/munge_sdp.js
index c2c7d9b6..70ed1e62 100644
--- a/chrome/test/data/webrtc/munge_sdp.js
+++ b/chrome/test/data/webrtc/munge_sdp.js
@@ -29,7 +29,7 @@
   var defaultCodec = getSdpDefaultAudioCodec(sdp);
   if (defaultCodec !== 'opus') {
     failure('setOpusDtxEnabled',
-             'Default audio codec is not set to \'opus\'.');
+            'Default audio codec is not set to \'opus\'.');
   }
 
   // Find codec ID for Opus, e.g. 111 if 'a=rtpmap:111 opus/48000/2'.
@@ -64,14 +64,15 @@
   // Find codec ID, e.g. 100 for 'VP8' if 'a=rtpmap:100 VP8/9000'.
   var codecId = findRtpmapId(sdpLines, codec);
   if (codecId === null) {
-    failure('sdpPreferCodec', 'Unknown ID for |codec| = \'' + codec + '\'.');
+    failure('setSdpDefaultCodec',
+            'Unknown ID for |codec| = \'' + codec + '\'.');
   }
 
   // Find 'm=|type|' line, e.g. 'm=video 9 UDP/TLS/RTP/SAVPF 100 101 107 116'.
   var mLineNo = findLine(sdpLines, 'm=' + type);
   if (mLineNo === null) {
     failure('setSdpDefaultCodec',
-             '\'m=' + type + '\' line missing from |sdp|.');
+            '\'m=' + type + '\' line missing from |sdp|.');
   }
 
   // Modify video line to use the desired codec as the default.
@@ -106,21 +107,21 @@
   var mLineNo = findLine(sdpLines, 'm=' + type);
   if (mLineNo === null) {
     failure('getSdpDefaultCodec',
-             '\'m=' + type + '\' line missing from |sdp|.');
+            '\'m=' + type + '\' line missing from |sdp|.');
   }
 
   // The default codec's ID.
   var defaultCodecId = getMLineDefaultCodec(sdpLines[mLineNo]);
   if (defaultCodecId === null) {
     failure('getSdpDefaultCodec',
-             '\'m=' + type + '\' line contains no codecs.');
+            '\'m=' + type + '\' line contains no codecs.');
   }
 
   // Find codec name, e.g. 'VP8' for 100 if 'a=rtpmap:100 VP8/9000'.
   var defaultCodec = findRtpmapCodec(sdpLines, defaultCodecId);
   if (defaultCodec === null) {
     failure('getSdpDefaultCodec',
-             'Unknown codec name for default codec ' + defaultCodecId + '.');
+            'Unknown codec name for default codec ' + defaultCodecId + '.');
   }
   return defaultCodec;
 }
@@ -244,9 +245,9 @@
 }
 
 /** @private */
-function findLine(lines, startsWith) {
-  for (var i = 0; i < lines.length; i++) {
-    if (lines[i].startsWith(startsWith))
+function findLine(lines, lineStartsWith, startingLine = 0) {
+  for (var i = startingLine; i < lines.length; i++) {
+    if (lines[i].startsWith(lineStartsWith))
       return i;
   }
   return null;
diff --git a/chrome/test/data/webrtc/peerconnection.js b/chrome/test/data/webrtc/peerconnection.js
index 22963b4..c51ff11 100644
--- a/chrome/test/data/webrtc/peerconnection.js
+++ b/chrome/test/data/webrtc/peerconnection.js
@@ -46,6 +46,9 @@
  */
 var gOpusDtx = false;
 
+/** @private */
+var gNegotiationNeededCount = 0;
+
 // Public interface to tests. These are expected to be called with
 // ExecuteJavascript invocations from the browser tests and will return answers
 // through the DOM automation controller.
@@ -454,6 +457,17 @@
   returnToTest(gIceGatheringState);
 }
 
+/**
+ * Returns "ok-negotiation-count-is-" followed by the number of times
+ * onnegotiationneeded has fired. This will include any currently queued
+ * negotiationneeded events.
+ */
+function getNegotiationNeededCount() {
+  window.setTimeout(function() {
+    returnToTest('ok-negotiation-count-is-' + gNegotiationNeededCount);
+  }, 0);
+}
+
 // Internals.
 
 /** @private */
@@ -467,6 +481,7 @@
   peerConnection.onremovestream = removeStreamCallback_;
   peerConnection.onicecandidate = iceCallback_;
   peerConnection.onicegatheringstatechange = iceGatheringCallback_;
+  peerConnection.onnegotiationneeded = negotiationNeededCallback_;
   return peerConnection;
 }
 
@@ -488,6 +503,10 @@
   gIceGatheringState = peerConnection.iceGatheringState;
 }
 
+/** @private */
+function negotiationNeededCallback_() {
+  ++gNegotiationNeededCount;
+}
 
 /** @private */
 function setLocalDescription(peerConnection, sessionDescription) {
diff --git a/chrome/test/data/webrtc/peerconnection_rtp.js b/chrome/test/data/webrtc/peerconnection_rtp.js
index 49962de..638ae8a 100644
--- a/chrome/test/data/webrtc/peerconnection_rtp.js
+++ b/chrome/test/data/webrtc/peerconnection_rtp.js
@@ -45,15 +45,6 @@
     throw failTest('One getSenders() call is not equal to the next.');
   }
 
-  let localTracks = new Set();
-  peerConnection_().getLocalStreams().forEach(function(stream) {
-    stream.getTracks().forEach(function(track) {
-      localTracks.add(track);
-    });
-  });
-  if (peerConnection_().getSenders().length != localTracks.size)
-    throw failTest('The number of senders and tracks are not the same.');
-
   let senders = new Set();
   let senderTracks = new Set();
   peerConnection_().getSenders().forEach(function(sender) {
@@ -67,10 +58,6 @@
   if (senderTracks.size != senders.size)
     throw failTest('senderTracks.size != senders.size');
 
-  if (!setEquals_(senderTracks, localTracks)) {
-    throw failTest('The set of sender tracks is not equal to the set of ' +
-                   'stream tracks.');
-  }
   returnToTest('ok-senders-verified');
 }
 
@@ -93,15 +80,6 @@
     throw failTest('One getReceivers() call is not equal to the next.');
   }
 
-  let remoteTracks = new Set();
-  peerConnection_().getRemoteStreams().forEach(function(stream) {
-    stream.getTracks().forEach(function(track) {
-      remoteTracks.add(track);
-    });
-  });
-  if (peerConnection_().getReceivers().length != remoteTracks.size)
-    throw failTest('The number of receivers and tracks are not the same.');
-
   let receivers = new Set();
   let receiverTracks = new Set();
   peerConnection_().getReceivers().forEach(function(receiver) {
@@ -117,16 +95,169 @@
   if (receiverTracks.size != receivers.size)
     throw failTest('receiverTracks.size != receivers.size');
 
-  if (!setEquals_(receiverTracks, remoteTracks)) {
-    throw failTest('The set of receiver tracks is not equal to the set of ' +
-                   'stream tracks.');
-  }
   returnToTest('ok-receivers-verified');
 }
 
+/**
+ * Creates an audio and video track and adds them to the peer connection using
+ * |addTrack|. They are added with or without a stream in accordance with
+ * |streamArgumentType|.
+ *
+ * Returns
+ * "ok-<audio stream id> <audio track id> <video stream id> <video track id>" on
+ * success. If no stream is backing up the track, <stream id> is "null".
+ *
+ * @param {string} streamArgumentType Must be one of the following values:
+ * 'no-stream' - The tracks are added without an associated stream.
+ * 'shared-stream' - The tracks are added with the same associated stream.
+ * 'individual-streams' - A stream is created for each track.
+ */
+function createAndAddAudioAndVideoTrack(streamArgumentType) {
+  if (streamArgumentType !== 'no-stream' &&
+      streamArgumentType !== 'shared-stream' &&
+      streamArgumentType !== 'individual-streams')
+    throw failTest('Unsupported streamArgumentType.');
+  getUserMedia({ audio: true, video: true },
+      function(stream) {
+        let audioStream = undefined;
+        if (streamArgumentType !== 'no-stream')
+          audioStream = new MediaStream();
+
+        let audioTrack = stream.getAudioTracks()[0];
+        let audioSender =
+            audioStream ? peerConnection_().addTrack(audioTrack, audioStream)
+                        : peerConnection_().addTrack(audioTrack);
+        if (!audioSender || audioSender.track != audioTrack)
+          throw failTest('addTrack did not return a sender with the track.');
+
+        let videoStream = undefined;
+        if (streamArgumentType === 'shared-stream') {
+          videoStream = audioStream;
+        } else if (streamArgumentType === 'individual-streams') {
+          videoStream = new MediaStream();
+        }
+
+        let videoTrack = stream.getVideoTracks()[0];
+        let videoSender =
+            videoStream ? peerConnection_().addTrack(videoTrack, videoStream)
+                        : peerConnection_().addTrack(videoTrack);
+        if (!videoSender || videoSender.track != videoTrack)
+          throw failTest('addTrack did not return a sender with the track.');
+
+        let audioStreamId = audioStream ? audioStream.id : 'null';
+        let videoStreamId = videoStream ? videoStream.id : 'null';
+        returnToTest('ok-' + audioStreamId + ' ' + audioTrack.id
+                     + ' ' + videoStreamId + ' ' + videoTrack.id);
+      },
+      function(error) {
+        throw failTest('getUserMedia failed: ' + error);
+      });
+}
+
+/**
+ * Calls |removeTrack| with the first sender that has the track with |trackId|
+ * and verifies the SDP is updated accordingly.
+ *
+ * Returns "ok-sender-removed" on success.
+ */
+function removeTrack(trackId) {
+  let sender = null;
+  let otherSenderHasTrack = false;
+  peerConnection_().getSenders().forEach(function(s) {
+    if (s.track && s.track.id == trackId) {
+      if (!sender)
+        sender = s;
+      else
+        otherSenderHasTrack = true;
+    }
+  });
+  if (!sender)
+    throw failTest('There is no sender for track ' + trackId);
+  peerConnection_().removeTrack(sender);
+  if (sender.track)
+    throw failTest('sender.track was not nulled by removeTrack.');
+  returnToTest('ok-sender-removed');
+}
+
+/**
+ * Returns "ok-stream-with-track-found" or "ok-stream-with-track-not-found".
+ * If |streamId| is null then any stream having a track with |trackId| will do.
+ */
+function hasLocalStreamWithTrack(streamId, trackId) {
+  if (hasStreamWithTrack(
+          peerConnection_().getLocalStreams(), streamId, trackId)) {
+    returnToTest('ok-stream-with-track-found');
+    return;
+  }
+  returnToTest('ok-stream-with-track-not-found');
+}
+
+/**
+ * Returns "ok-stream-with-track-found" or "ok-stream-with-track-not-found".
+ * If |streamId| is null then any stream having a track with |trackId| will do.
+ */
+function hasRemoteStreamWithTrack(streamId, trackId) {
+  if (hasStreamWithTrack(
+          peerConnection_().getRemoteStreams(), streamId, trackId)) {
+    returnToTest('ok-stream-with-track-found');
+    return;
+  }
+  returnToTest('ok-stream-with-track-not-found');
+}
+
+/**
+ * Returns "ok-sender-with-track-found" or "ok-sender-with-track-not-found".
+ */
+function hasSenderWithTrack(trackId) {
+  if (hasSenderOrReceiverWithTrack(peerConnection_().getSenders(), trackId)) {
+    returnToTest('ok-sender-with-track-found');
+    return;
+  }
+  returnToTest('ok-sender-with-track-not-found');
+}
+
+/**
+ * Returns "ok-receiver-with-track-found" or "ok-receiver-with-track-not-found".
+ */
+function hasReceiverWithTrack(trackId) {
+  if (hasSenderOrReceiverWithTrack(peerConnection_().getReceivers(), trackId)) {
+    returnToTest('ok-receiver-with-track-found');
+    return;
+  }
+  returnToTest('ok-receiver-with-track-not-found');
+}
+
 // Internals.
 
 /** @private */
+function hasStreamWithTrack(streams, streamId, trackId) {
+  for (let i = 0; i < streams.length; ++i) {
+    let stream = streams[i];
+    if (streamId && stream.id !== streamId)
+      continue;
+    let tracks = stream.getTracks();
+    for (let j = 0; j < tracks.length; ++j) {
+      let track = tracks[j];
+      if (track.id == trackId) {
+        return true;
+      }
+    }
+  }
+  return false;
+}
+
+/** @private */
+function hasSenderOrReceiverWithTrack(sendersOrReceivers, trackId) {
+  for (let i = 0; i < sendersOrReceivers.length; ++i) {
+    if (sendersOrReceivers[i].track &&
+        sendersOrReceivers[i].track.id === trackId) {
+      return true;
+    }
+  }
+  return false;
+}
+
+/** @private */
 function arrayEquals_(a, b) {
   if (a == null)
     return b == null;
diff --git a/components/arc/arc_service_manager.h b/components/arc/arc_service_manager.h
index bd75a2d9..26540b0 100644
--- a/components/arc/arc_service_manager.h
+++ b/components/arc/arc_service_manager.h
@@ -18,6 +18,10 @@
 #include "components/arc/arc_service.h"
 #include "components/arc/intent_helper/local_activity_resolver.h"
 
+namespace content {
+class BrowserContext;
+}  // namespace content
+
 namespace arc {
 
 class ArcBridgeService;
@@ -63,6 +67,18 @@
   ArcServiceManager();
   ~ArcServiceManager();
 
+  // Returns the current BrowserContext which ARC is allowed.
+  // This is workaround to split the dependency from chrome/.
+  // TODO(hidehiko): Remove this when we move IsArcAllowedForProfile() to
+  // components/arc.
+  content::BrowserContext* browser_context() { return browser_context_; }
+
+  // TODO(hidehiko): Remove this when we move IsArcAllowedForProfile() to
+  // components/arc. See browser_context() for details.
+  void set_browser_context(content::BrowserContext* browser_context) {
+    browser_context_ = browser_context;
+  }
+
   // |arc_bridge_service| can only be accessed on the thread that this
   // class was created on.
   ArcBridgeService* arc_bridge_service();
@@ -119,6 +135,17 @@
   std::unordered_multimap<std::string, std::unique_ptr<ArcService>> services_;
   scoped_refptr<LocalActivityResolver> activity_resolver_;
 
+  // This holds the pointer to the BrowserContext (practically Profile)
+  // which is allowed to use ARC.
+  // This is set just before BrowserContextKeyedService classes are
+  // instantiated.
+  // So, in BrowserContextKeyedServiceFactory::BuildServiceInstanceFor(),
+  // given BrowserContext pointer can be compared to this to check if it is
+  // allowed to use ARC.
+  // TODO(hidehiko): Remove this when we move IsArcAllowedForProfile() to
+  // components/arc. See browser_context() for details.
+  content::BrowserContext* browser_context_ = nullptr;
+
   DISALLOW_COPY_AND_ASSIGN(ArcServiceManager);
 };
 
diff --git a/components/cronet/ios/Cronet.h b/components/cronet/ios/Cronet.h
index 920cace..9275b45 100644
--- a/components/cronet/ios/Cronet.h
+++ b/components/cronet/ios/Cronet.h
@@ -51,6 +51,10 @@
 // any effect before |start| is called.
 + (void)setQuicEnabled:(BOOL)quicEnabled;
 
+// Sets whether Brotli should be supported by CronetEngine. This method only has
+// any effect before |start| is called.
++ (void)setBrotliEnabled:(BOOL)brotliEnabled;
+
 // Set HTTP Cache type to be used by CronetEngine.  This method only has any
 // effect before |start| is called.  See HttpCacheType enum for available
 // options.
diff --git a/components/cronet/ios/Cronet.mm b/components/cronet/ios/Cronet.mm
index e7cc88b..5c4e76c 100644
--- a/components/cronet/ios/Cronet.mm
+++ b/components/cronet/ios/Cronet.mm
@@ -43,6 +43,7 @@
 // sane.
 BOOL gHttp2Enabled = YES;
 BOOL gQuicEnabled = NO;
+BOOL gBrotliEnabled = NO;
 cronet::URLRequestContextConfig::HttpCacheType gHttpCache =
     cronet::URLRequestContextConfig::HttpCacheType::DISK;
 QuicHintVector gQuicHints;
@@ -179,6 +180,11 @@
   gQuicEnabled = quicEnabled;
 }
 
++ (void)setBrotliEnabled:(BOOL)brotliEnabled {
+  [self checkNotStarted];
+  gBrotliEnabled = brotliEnabled;
+}
+
 + (void)addQuicHint:(NSString*)host port:(int)port altPort:(int)altPort {
   [self checkNotStarted];
   gQuicHints.push_back(
@@ -281,6 +287,7 @@
 
   gChromeNet.Get()->set_http2_enabled(gHttp2Enabled);
   gChromeNet.Get()->set_quic_enabled(gQuicEnabled);
+  gChromeNet.Get()->set_brotli_enabled(gBrotliEnabled);
   gChromeNet.Get()->set_experimental_options(
       base::SysNSStringToUTF8(gExperimentalOptions));
   gChromeNet.Get()->set_http_cache(gHttpCache);
diff --git a/components/cronet/ios/cronet_environment.h b/components/cronet/ios/cronet_environment.h
index 8f4adc7..bed027b 100644
--- a/components/cronet/ios/cronet_environment.h
+++ b/components/cronet/ios/cronet_environment.h
@@ -68,13 +68,15 @@
   void AddQuicHint(const std::string& host, int port, int alternate_port);
 
   // Setters and getters for |http2_enabled_|, |quic_enabled_|, and
-  // |forced_quic_origin_|. These only have any effect before Start() is
-  // called.
+  // |brotli_enabled| These only have any effect
+  // before Start() is called.
   void set_http2_enabled(bool enabled) { http2_enabled_ = enabled; }
   void set_quic_enabled(bool enabled) { quic_enabled_ = enabled; }
+  void set_brotli_enabled(bool enabled) { brotli_enabled_ = enabled; }
 
   bool http2_enabled() const { return http2_enabled_; }
   bool quic_enabled() const { return quic_enabled_; }
+  bool brotli_enabled() const { return brotli_enabled_; }
 
   void set_quic_user_agent_id(const std::string& quic_user_agent_id) {
     quic_user_agent_id_ = quic_user_agent_id;
@@ -145,6 +147,7 @@
 
   bool http2_enabled_;
   bool quic_enabled_;
+  bool brotli_enabled_;
   std::string quic_user_agent_id_;
   std::string accept_language_;
   std::string experimental_options_;
diff --git a/components/cronet/ios/cronet_environment.mm b/components/cronet/ios/cronet_environment.mm
index 1e04229..dd3c96f 100644
--- a/components/cronet/ios/cronet_environment.mm
+++ b/components/cronet/ios/cronet_environment.mm
@@ -219,6 +219,7 @@
                                      bool user_agent_partial)
     : http2_enabled_(false),
       quic_enabled_(false),
+      brotli_enabled_(false),
       http_cache_(URLRequestContextConfig::HttpCacheType::DISK),
       user_agent_(user_agent),
       user_agent_partial_(user_agent_partial),
@@ -348,6 +349,7 @@
 
   context_builder.SetHttpServerProperties(std::move(http_server_properties));
 
+  context_builder.set_enable_brotli(brotli_enabled_);
   main_context_ = context_builder.Build();
 
   main_context_->transport_security_state()
diff --git a/components/cronet/ios/test/cronet_http_test.mm b/components/cronet/ios/test/cronet_http_test.mm
index 99feb490..e94154a6 100644
--- a/components/cronet/ios/test/cronet_http_test.mm
+++ b/components/cronet/ios/test/cronet_http_test.mm
@@ -372,4 +372,50 @@
   EXPECT_TRUE([[delegate_ responseBody] containsString:testString]);
 }
 
+TEST_F(HttpTest, BrotliAdvertisedTest) {
+  [Cronet shutdownForTesting];
+
+  [Cronet setBrotliEnabled:YES];
+
+  StartCronet(grpc_support::GetQuicTestServerPort());
+
+  NSURL* url =
+      net::NSURLWithGURL(GURL(TestServer::GetEchoHeaderURL("Accept-Encoding")));
+  NSURLSessionDataTask* task = [session_ dataTaskWithURL:url];
+  StartDataTaskAndWaitForCompletion(task);
+  EXPECT_EQ(nil, [delegate_ error]);
+  EXPECT_TRUE([[delegate_ responseBody] containsString:@"br"]);
+}
+
+TEST_F(HttpTest, BrotliNotAdvertisedTest) {
+  [Cronet shutdownForTesting];
+
+  [Cronet setBrotliEnabled:NO];
+
+  StartCronet(grpc_support::GetQuicTestServerPort());
+
+  NSURL* url =
+      net::NSURLWithGURL(GURL(TestServer::GetEchoHeaderURL("Accept-Encoding")));
+  NSURLSessionDataTask* task = [session_ dataTaskWithURL:url];
+  StartDataTaskAndWaitForCompletion(task);
+  EXPECT_EQ(nil, [delegate_ error]);
+  EXPECT_FALSE([[delegate_ responseBody] containsString:@"br"]);
+}
+
+TEST_F(HttpTest, BrotliHandleDecoding) {
+  [Cronet shutdownForTesting];
+
+  [Cronet setBrotliEnabled:YES];
+
+  StartCronet(grpc_support::GetQuicTestServerPort());
+
+  NSURL* url =
+      net::NSURLWithGURL(GURL(TestServer::GetUseEncodingURL("brotli")));
+  NSURLSessionDataTask* task = [session_ dataTaskWithURL:url];
+  StartDataTaskAndWaitForCompletion(task);
+  EXPECT_EQ(nil, [delegate_ error]);
+  EXPECT_STREQ(base::SysNSStringToUTF8([delegate_ responseBody]).c_str(),
+               "The quick brown fox jumps over the lazy dog");
+}
+
 }  // namespace cronet
diff --git a/components/cronet/ios/test/test_server.cc b/components/cronet/ios/test/test_server.cc
index 0a5385b..d41fa248 100644
--- a/components/cronet/ios/test/test_server.cc
+++ b/components/cronet/ios/test/test_server.cc
@@ -22,6 +22,7 @@
 const char kEchoHeaderPath[] = "/EchoHeader?";
 const char kSetCookiePath[] = "/SetCookie?";
 const char kBigDataPath[] = "/BigData?";
+const char kUseEncodingPath[] = "/UseEncoding?";
 
 std::unique_ptr<net::EmbeddedTestServer> g_test_server;
 base::LazyInstance<std::string>::Leaky g_big_data_body =
@@ -43,6 +44,29 @@
   return std::move(http_response);
 }
 
+std::unique_ptr<net::test_server::HttpResponse> UseEncodingInResponse(
+    const net::test_server::HttpRequest& request) {
+  std::string encoding;
+  DCHECK(base::StartsWith(request.relative_url, kUseEncodingPath,
+                          base::CompareCase::INSENSITIVE_ASCII));
+
+  encoding = request.relative_url.substr(strlen(kUseEncodingPath));
+  auto http_response = base::MakeUnique<net::test_server::BasicHttpResponse>();
+  if (!encoding.compare("brotli")) {
+    const char quickfoxCompressed[] = {
+        0x0b, 0x15, -0x80, 0x54, 0x68, 0x65, 0x20, 0x71, 0x75, 0x69, 0x63, 0x6b,
+        0x20, 0x62, 0x72,  0x6f, 0x77, 0x6e, 0x20, 0x66, 0x6f, 0x78, 0x20, 0x6a,
+        0x75, 0x6d, 0x70,  0x73, 0x20, 0x6f, 0x76, 0x65, 0x72, 0x20, 0x74, 0x68,
+        0x65, 0x20, 0x6c,  0x61, 0x7a, 0x79, 0x20, 0x64, 0x6f, 0x67, 0x03};
+    std::string quickfoxCompressedStr(quickfoxCompressed,
+                                      sizeof(quickfoxCompressed));
+    http_response->set_content(quickfoxCompressedStr);
+    http_response->AddCustomHeader(std::string("content-encoding"),
+                                   std::string("br"));
+  }
+  return std::move(http_response);
+}
+
 std::unique_ptr<net::test_server::HttpResponse> ReturnBigDataInResponse(
     const net::test_server::HttpRequest& request) {
   DCHECK(base::StartsWith(request.relative_url, kBigDataPath,
@@ -81,6 +105,10 @@
                        base::CompareCase::INSENSITIVE_ASCII)) {
     return ReturnBigDataInResponse(request);
   }
+  if (base::StartsWith(request.relative_url, kUseEncodingPath,
+                       base::CompareCase::INSENSITIVE_ASCII)) {
+    return UseEncodingInResponse(request);
+  }
   return base::MakeUnique<net::test_server::BasicHttpResponse>();
 }
 
@@ -108,6 +136,11 @@
   return g_test_server->GetURL(kEchoHeaderPath + header_name).spec();
 }
 
+std::string TestServer::GetUseEncodingURL(const std::string& header_name) {
+  DCHECK(g_test_server);
+  return g_test_server->GetURL(kUseEncodingPath + header_name).spec();
+}
+
 std::string TestServer::GetSetCookieURL(const std::string& cookie_line) {
   DCHECK(g_test_server);
   return g_test_server->GetURL(kSetCookiePath + cookie_line).spec();
diff --git a/components/cronet/ios/test/test_server.h b/components/cronet/ios/test/test_server.h
index a0b7b8a..668dbe7 100644
--- a/components/cronet/ios/test/test_server.h
+++ b/components/cronet/ios/test/test_server.h
@@ -17,6 +17,9 @@
   // Returns URL which respond with echo of header with |header_name| in
   // response body.
   static std::string GetEchoHeaderURL(const std::string& header_name);
+  // Returns URL which responds with "The quick brown fox jumps over the lazy
+  // dog" in specified encoding.
+  static std::string GetUseEncodingURL(const std::string& header_name);
   // Returns URL which respond with setting cookie to |cookie_line| and echo it
   // in response body.
   static std::string GetSetCookieURL(const std::string& cookie_line);
diff --git a/components/cronet/tools/cr_cronet.py b/components/cronet/tools/cr_cronet.py
index d424b93a..891c895 100755
--- a/components/cronet/tools/cr_cronet.py
+++ b/components/cronet/tools/cr_cronet.py
@@ -101,6 +101,7 @@
 
   gn_args += 'target_os="' + target_os + '" enable_websockets=false '+ \
       'disable_file_support=true disable_ftp_support=true '+ \
+      'disable_brotli_filter=false ' + \
       'use_platform_icu_alternatives=true '+ \
       'enable_reporting=false '+ \
       'is_component_build=false ' + \
diff --git a/components/ntp_snippets/remote/remote_suggestions_provider_impl.cc b/components/ntp_snippets/remote/remote_suggestions_provider_impl.cc
index 5469ed3..877d27d 100644
--- a/components/ntp_snippets/remote/remote_suggestions_provider_impl.cc
+++ b/components/ntp_snippets/remote/remote_suggestions_provider_impl.cc
@@ -12,6 +12,7 @@
 #include "base/command_line.h"
 #include "base/location.h"
 #include "base/memory/ptr_util.h"
+#include "base/metrics/field_trial_params.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/metrics/sparse_histogram.h"
 #include "base/stl_util.h"
@@ -60,15 +61,26 @@
 const char kOrderNewRemoteCategoriesBasedOnArticlesCategory[] =
     "order_new_remote_categories_based_on_articles_category";
 
-// Not more than this number of prefetched suggestions will be kept longer.
-const int kMaxAdditionalPrefetchedSuggestions = 5;
+// Variation parameter for additional prefetched suggestions quantity. Not more
+// than this number of prefetched suggestions will be kept longer.
+const char kMaxAdditionalPrefetchedSuggestionsParamName[] =
+    "max_additional_prefetched_suggestions";
 
-// Only prefetched suggestions published not later than this are considered to
-// be kept longer.
-const base::TimeDelta kMaxAgeForAdditionalPrefetchedSuggestion =
+const int kDefaultMaxAdditionalPrefetchedSuggestions = 5;
+
+// Variation parameter for additional prefetched suggestions age. Only
+// prefetched suggestions fetched not later than this are considered to be kept
+// longer.
+const char kMaxAgeForAdditionalPrefetchedSuggestionParamName[] =
+    "max_age_for_additional_prefetched_suggestion_minutes";
+
+const base::TimeDelta kDefaultMaxAgeForAdditionalPrefetchedSuggestion =
     base::TimeDelta::FromHours(36);
 
 bool IsOrderingNewRemoteCategoriesBasedOnArticlesCategoryEnabled() {
+  // TODO(vitaliii): Use GetFieldTrialParamByFeature(As.*)? from
+  // base/metrics/field_trial_params.h. GetVariationParamByFeature(As.*)? are
+  // deprecated.
   return variations::GetVariationParamByFeatureAsBool(
       ntp_snippets::kArticleSuggestionsFeature,
       kOrderNewRemoteCategoriesBasedOnArticlesCategory,
@@ -107,6 +119,20 @@
   return base::FeatureList::IsEnabled(kKeepPrefetchedContentSuggestions);
 }
 
+int GetMaxAdditionalPrefetchedSuggestions() {
+  return base::GetFieldTrialParamByFeatureAsInt(
+      kKeepPrefetchedContentSuggestions,
+      kMaxAdditionalPrefetchedSuggestionsParamName,
+      kDefaultMaxAdditionalPrefetchedSuggestions);
+}
+
+base::TimeDelta GetMaxAgeForAdditionalPrefetchedSuggestion() {
+  return base::TimeDelta::FromMinutes(base::GetFieldTrialParamByFeatureAsInt(
+      kKeepPrefetchedContentSuggestions,
+      kMaxAgeForAdditionalPrefetchedSuggestionParamName,
+      kDefaultMaxAgeForAdditionalPrefetchedSuggestion.InMinutes()));
+}
+
 template <typename SuggestionPtrContainer>
 std::unique_ptr<std::vector<std::string>> GetSuggestionIDVector(
     const SuggestionPtrContainer& suggestions) {
@@ -808,9 +834,9 @@
                             : remote_suggestion->amp_url();
       if (prefetched_pages_tracker_->PrefetchedOfflinePageExists(url) &&
           clock_->Now() - remote_suggestion->fetch_date() <
-              kMaxAgeForAdditionalPrefetchedSuggestion &&
-          additional_prefetched_suggestions.size() <
-              kMaxAdditionalPrefetchedSuggestions) {
+              GetMaxAgeForAdditionalPrefetchedSuggestion() &&
+          static_cast<int>(additional_prefetched_suggestions.size()) <
+              GetMaxAdditionalPrefetchedSuggestions()) {
         additional_prefetched_suggestions.push_back(
             std::move(remote_suggestion));
       } else {
diff --git a/components/ntp_snippets/remote/remote_suggestions_provider_impl_unittest.cc b/components/ntp_snippets/remote/remote_suggestions_provider_impl_unittest.cc
index e2a2914..b251491 100644
--- a/components/ntp_snippets/remote/remote_suggestions_provider_impl_unittest.cc
+++ b/components/ntp_snippets/remote/remote_suggestions_provider_impl_unittest.cc
@@ -114,10 +114,11 @@
 
 const int kUnknownRemoteCategoryId = 1234;
 
-const int kMaxAdditionalPrefetchedSuggestions = 5;
-
+// Different from default values to confirm that variation param values are
+// used.
+const int kMaxAdditionalPrefetchedSuggestions = 7;
 const base::TimeDelta kMaxAgeForAdditionalPrefetchedSuggestion =
-    base::TimeDelta::FromHours(36);
+    base::TimeDelta::FromHours(48);
 
 base::Time GetDefaultCreationTime() {
   base::Time out_time;
@@ -619,14 +620,22 @@
         {kArticleSuggestionsFeature.name});
   }
 
-  void EnableKeepingPrefetchedContentSuggestions() {
+  void EnableKeepingPrefetchedContentSuggestions(
+      int max_additional_prefetched_suggestions,
+      const base::TimeDelta& max_age_for_additional_prefetched_suggestion) {
     // params_manager supports only one
     // |SetVariationParamsWithFeatureAssociations| at a time, so we clear
     // previous settings first to make this explicit.
     params_manager_.ClearAllVariationParams();
     params_manager_.SetVariationParamsWithFeatureAssociations(
         kKeepPrefetchedContentSuggestions.name,
-        /*param_values=*/std::map<std::string, std::string>(),
+        {
+            {"max_additional_prefetched_suggestions",
+             base::IntToString(max_additional_prefetched_suggestions)},
+            {"max_age_for_additional_prefetched_suggestion_minutes",
+             base::IntToString(
+                 max_age_for_additional_prefetched_suggestion.InMinutes())},
+        },
         {kKeepPrefetchedContentSuggestions.name});
   }
 
@@ -2469,7 +2478,10 @@
 
 TEST_F(RemoteSuggestionsProviderImplTest,
        ShouldFetchNormallyWithoutPrefetchedPagesTracker) {
-  EnableKeepingPrefetchedContentSuggestions();
+  EnableKeepingPrefetchedContentSuggestions(
+      kMaxAdditionalPrefetchedSuggestions,
+      kMaxAgeForAdditionalPrefetchedSuggestion);
+
   auto provider = MakeSuggestionsProvider();
   std::vector<FetchedCategory> fetched_categories;
   fetched_categories.push_back(
@@ -2486,7 +2498,10 @@
 
 TEST_F(RemoteSuggestionsProviderImplTest,
        ShouldKeepPrefetchedSuggestionsAfterFetchWhenEnabled) {
-  EnableKeepingPrefetchedContentSuggestions();
+  EnableKeepingPrefetchedContentSuggestions(
+      kMaxAdditionalPrefetchedSuggestions,
+      kMaxAgeForAdditionalPrefetchedSuggestion);
+
   auto provider = MakeSuggestionsProviderWithoutInitialization(
       /*use_mock_prefetched_pages_tracker=*/true);
   auto* mock_tracker = static_cast<StrictMock<MockPrefetchedPagesTracker>*>(
@@ -2536,7 +2551,10 @@
 
 TEST_F(RemoteSuggestionsProviderImplTest,
        ShouldIgnoreNotPrefetchedSuggestionsAfterFetchWhenEnabled) {
-  EnableKeepingPrefetchedContentSuggestions();
+  EnableKeepingPrefetchedContentSuggestions(
+      kMaxAdditionalPrefetchedSuggestions,
+      kMaxAgeForAdditionalPrefetchedSuggestion);
+
   auto provider = MakeSuggestionsProviderWithoutInitialization(
       /*use_mock_prefetched_pages_tracker=*/true);
   auto* mock_tracker = static_cast<StrictMock<MockPrefetchedPagesTracker>*>(
@@ -2584,7 +2602,10 @@
 
 TEST_F(RemoteSuggestionsProviderImplTest,
        ShouldLimitKeptPrefetchedSuggestionsAfterFetchWhenEnabled) {
-  EnableKeepingPrefetchedContentSuggestions();
+  EnableKeepingPrefetchedContentSuggestions(
+      kMaxAdditionalPrefetchedSuggestions,
+      kMaxAgeForAdditionalPrefetchedSuggestion);
+
   auto provider = MakeSuggestionsProviderWithoutInitialization(
       /*use_mock_prefetched_pages_tracker=*/true);
   auto* mock_tracker = static_cast<StrictMock<MockPrefetchedPagesTracker>*>(
@@ -2638,7 +2659,10 @@
 
 TEST_F(RemoteSuggestionsProviderImplTest,
        ShouldMixInPrefetchedSuggestionsByScoreAfterFetchWhenEnabled) {
-  EnableKeepingPrefetchedContentSuggestions();
+  EnableKeepingPrefetchedContentSuggestions(
+      kMaxAdditionalPrefetchedSuggestions,
+      kMaxAgeForAdditionalPrefetchedSuggestion);
+
   auto provider = MakeSuggestionsProviderWithoutInitialization(
       /*use_mock_prefetched_pages_tracker=*/true);
   auto* mock_tracker = static_cast<StrictMock<MockPrefetchedPagesTracker>*>(
@@ -2708,7 +2732,10 @@
 TEST_F(
     RemoteSuggestionsProviderImplTest,
     ShouldKeepMostRecentlyFetchedPrefetchedSuggestionsFirstAfterFetchWhenEnabled) {
-  EnableKeepingPrefetchedContentSuggestions();
+  EnableKeepingPrefetchedContentSuggestions(
+      kMaxAdditionalPrefetchedSuggestions,
+      kMaxAgeForAdditionalPrefetchedSuggestion);
+
   auto provider = MakeSuggestionsProviderWithoutInitialization(
       /*use_mock_prefetched_pages_tracker=*/true);
   auto* mock_tracker = static_cast<StrictMock<MockPrefetchedPagesTracker>*>(
@@ -2761,7 +2788,10 @@
 
 TEST_F(RemoteSuggestionsProviderImplTest,
        ShouldNotKeepStalePrefetchedSuggestionsAfterFetchWhenEnabled) {
-  EnableKeepingPrefetchedContentSuggestions();
+  EnableKeepingPrefetchedContentSuggestions(
+      kMaxAdditionalPrefetchedSuggestions,
+      kMaxAgeForAdditionalPrefetchedSuggestion);
+
   auto provider = MakeSuggestionsProviderWithoutInitialization(
       /*use_mock_prefetched_pages_tracker=*/true);
   auto* mock_tracker = static_cast<StrictMock<MockPrefetchedPagesTracker>*>(
@@ -2848,7 +2878,10 @@
 
 TEST_F(RemoteSuggestionsProviderImplTest,
        ShouldWaitForPrefetchedPagesTrackerInitialization) {
-  EnableKeepingPrefetchedContentSuggestions();
+  EnableKeepingPrefetchedContentSuggestions(
+      kMaxAdditionalPrefetchedSuggestions,
+      kMaxAgeForAdditionalPrefetchedSuggestion);
+
   auto provider = MakeSuggestionsProviderWithoutInitialization(
       /*use_mock_prefetched_pages_tracker=*/true);
   auto* mock_tracker = static_cast<StrictMock<MockPrefetchedPagesTracker>*>(
diff --git a/components/ntp_tiles/most_visited_sites.cc b/components/ntp_tiles/most_visited_sites.cc
index adf31a3..45a4bcdd 100644
--- a/components/ntp_tiles/most_visited_sites.cc
+++ b/components/ntp_tiles/most_visited_sites.cc
@@ -33,6 +33,9 @@
 const base::Feature kDisplaySuggestionsServiceTiles{
     "DisplaySuggestionsServiceTiles", base::FEATURE_ENABLED_BY_DEFAULT};
 
+// The maximum index of the home page tile.
+const size_t kMaxHomeTileIndex = 3;
+
 // Determine whether we need any tiles from PopularSites to fill up a grid of
 // |num_tiles| tiles.
 bool NeedPopularSites(const PrefService* prefs, int num_tiles) {
@@ -401,24 +404,39 @@
 
   const GURL& home_page_url = home_page_client_->GetHomePageUrl();
   NTPTilesVector new_tiles;
-  // Add the home tile as first tile.
+  // Add the home tile to the first four tiles.
   NTPTile home_tile;
   home_tile.url = home_page_url;
   home_tile.title = title;
   home_tile.source = TileSource::HOMEPAGE;
-  new_tiles.push_back(std::move(home_tile));
 
-  for (auto& tile : tiles) {
-    if (new_tiles.size() >= num_sites_) {
-      break;
-    }
+  bool home_tile_added = false;
+  size_t index = 0;
 
+  while (index < tiles.size() && new_tiles.size() < num_sites_) {
+    bool hosts_are_equal = tiles[index].url.host() == home_page_url.host();
+
+    // Add the home tile to the first four tiles
+    // or at the position of a tile that has the same host
+    // and is ranked higher.
     // TODO(fhorschig): Introduce a more sophisticated deduplication.
-    if (tile.url.host() == home_page_url.host()) {
-      continue;
+    if (!home_tile_added && (index >= kMaxHomeTileIndex || hosts_are_equal)) {
+      new_tiles.push_back(std::move(home_tile));
+      home_tile_added = true;
+      continue;  // Do not advance the current tile index.
     }
 
-    new_tiles.push_back(std::move(tile));
+    // Add non-home page tiles.
+    if (!hosts_are_equal) {
+      new_tiles.push_back(std::move(tiles[index]));
+    }
+    ++index;
+  }
+
+  // Add the home page tile if there are less than 4 tiles
+  // and none of them is the home page (and there is space left).
+  if (!home_tile_added && new_tiles.size() < num_sites_) {
+    new_tiles.push_back(std::move(home_tile));
   }
   return new_tiles;
 }
diff --git a/components/ntp_tiles/most_visited_sites_unittest.cc b/components/ntp_tiles/most_visited_sites_unittest.cc
index a4458b1..43bdea7 100644
--- a/components/ntp_tiles/most_visited_sites_unittest.cc
+++ b/components/ntp_tiles/most_visited_sites_unittest.cc
@@ -66,6 +66,7 @@
 using testing::Not;
 using testing::Return;
 using testing::ReturnRef;
+using testing::SaveArg;
 using testing::SizeIs;
 using testing::StrictMock;
 using testing::_;
@@ -548,7 +549,7 @@
   base::RunLoop().RunUntilIdle();
 }
 
-TEST_P(MostVisitedSitesTest, ShouldReturnOnlyHomePageIfOneTileRequested) {
+TEST_P(MostVisitedSitesTest, ShouldReturnMostPopularPageIfOneTileRequested) {
   FakeHomePageClient* home_page_client = RegisterNewHomePageClient();
   home_page_client->SetHomePageEnabled(true);
   DisableRemoteSuggestions();
@@ -560,13 +561,39 @@
       .Times(AnyNumber())
       .WillRepeatedly(Return(false));
   EXPECT_CALL(mock_observer_,
-              OnMostVisitedURLsAvailable(ElementsAre(
-                  MatchesTile("", kHomePageUrl, TileSource::HOMEPAGE))));
+              OnMostVisitedURLsAvailable(ElementsAre(MatchesTile(
+                  "Site 1", "http://site1/", TileSource::TOP_SITES))));
   most_visited_sites_->SetMostVisitedURLsObserver(&mock_observer_,
                                                   /*num_sites=*/1);
   base::RunLoop().RunUntilIdle();
 }
 
+TEST_P(MostVisitedSitesTest, ShouldContainHomePageInFirstFourTiles) {
+  FakeHomePageClient* home_page_client = RegisterNewHomePageClient();
+  home_page_client->SetHomePageEnabled(true);
+  DisableRemoteSuggestions();
+  EXPECT_CALL(*mock_top_sites_, GetMostVisitedURLs(_, false))
+      .WillRepeatedly(InvokeCallbackArgument<0>((MostVisitedURLList{
+          MakeMostVisitedURL("Site 1", "http://site1/"),
+          MakeMostVisitedURL("Site 2", "http://site2/"),
+          MakeMostVisitedURL("Site 3", "http://site3/"),
+          MakeMostVisitedURL("Site 4", "http://site4/"),
+          MakeMostVisitedURL("Site 5", "http://site5/"),
+      })));
+  EXPECT_CALL(*mock_top_sites_, SyncWithHistory());
+  EXPECT_CALL(*mock_top_sites_, IsBlacklisted(Eq(GURL(kHomePageUrl))))
+      .Times(AnyNumber())
+      .WillRepeatedly(Return(false));
+  std::vector<NTPTile> tiles;
+  EXPECT_CALL(mock_observer_, OnMostVisitedURLsAvailable(_))
+      .WillOnce(SaveArg<0>(&tiles));
+  most_visited_sites_->SetMostVisitedURLsObserver(&mock_observer_,
+                                                  /*num_sites=*/8);
+  base::RunLoop().RunUntilIdle();
+  // Assert that the home page tile is in the first four tiles.
+  EXPECT_THAT(tiles[3], MatchesTile("", kHomePageUrl, TileSource::HOMEPAGE));
+}
+
 TEST_P(MostVisitedSitesTest, ShouldDeduplicateHomePageWithTopSites) {
   FakeHomePageClient* home_page_client = RegisterNewHomePageClient();
   home_page_client->SetHomePageEnabled(true);
@@ -580,10 +607,10 @@
       .Times(AnyNumber())
       .WillRepeatedly(Return(false));
   EXPECT_CALL(mock_observer_,
-              OnMostVisitedURLsAvailable(
-                  AllOf(FirstTileIs("", kHomePageUrl, TileSource::HOMEPAGE),
-                        Not(Contains(MatchesTile("", kHomePageUrl,
-                                                 TileSource::TOP_SITES))))));
+              OnMostVisitedURLsAvailable(AllOf(
+                  Contains(MatchesTile("", kHomePageUrl, TileSource::HOMEPAGE)),
+                  Not(Contains(
+                      MatchesTile("", kHomePageUrl, TileSource::TOP_SITES))))));
   most_visited_sites_->SetMostVisitedURLsObserver(&mock_observer_,
                                                   /*num_sites=*/3);
   base::RunLoop().RunUntilIdle();
diff --git a/components/password_manager/content/browser/BUILD.gn b/components/password_manager/content/browser/BUILD.gn
index c61b656c..d858a3c 100644
--- a/components/password_manager/content/browser/BUILD.gn
+++ b/components/password_manager/content/browser/BUILD.gn
@@ -24,7 +24,6 @@
     "//components/autofill/content/common:mojo_interfaces",
     "//components/autofill/core/common",
     "//components/keyed_service/content",
-    "//components/password_manager/content/common:mojo_interfaces",
     "//components/password_manager/core/browser",
     "//components/password_manager/core/common",
     "//components/prefs",
diff --git a/components/password_manager/content/browser/credential_manager_impl.h b/components/password_manager/content/browser/credential_manager_impl.h
index 4e6369df..8a4bf3d 100644
--- a/components/password_manager/content/browser/credential_manager_impl.h
+++ b/components/password_manager/content/browser/credential_manager_impl.h
@@ -10,7 +10,6 @@
 #include "base/callback.h"
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
-#include "components/password_manager/content/common/credential_manager.mojom.h"
 #include "components/password_manager/core/browser/credential_manager_password_form_manager.h"
 #include "components/password_manager/core/browser/credential_manager_pending_prevent_silent_access_task.h"
 #include "components/password_manager/core/browser/credential_manager_pending_request_task.h"
@@ -19,6 +18,7 @@
 #include "components/prefs/pref_member.h"
 #include "content/public/browser/web_contents_observer.h"
 #include "mojo/public/cpp/bindings/associated_binding.h"
+#include "third_party/WebKit/public/platform/modules/credentialmanager/credential_manager.mojom.h"
 
 class GURL;
 
diff --git a/components/password_manager/content/common/BUILD.gn b/components/password_manager/content/common/BUILD.gn
deleted file mode 100644
index 0fafcea..0000000
--- a/components/password_manager/content/common/BUILD.gn
+++ /dev/null
@@ -1,16 +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.
-
-import("//mojo/public/tools/bindings/mojom.gni")
-
-mojom("mojo_interfaces") {
-  sources = [
-    "credential_manager.mojom",
-  ]
-
-  public_deps = [
-    "//url/mojo:url_mojom_gurl",
-    "//url/mojo:url_mojom_origin",
-  ]
-}
diff --git a/components/password_manager/content/common/OWNERS b/components/password_manager/content/common/OWNERS
index 2c44a46..4df0c71 100644
--- a/components/password_manager/content/common/OWNERS
+++ b/components/password_manager/content/common/OWNERS
@@ -1,5 +1,3 @@
-per-file *.mojom=set noparent
-per-file *.mojom=file://ipc/SECURITY_OWNERS
 per-file *_struct_traits*.*=set noparent
 per-file *_struct_traits*.*=file://ipc/SECURITY_OWNERS
 per-file *.typemap=set noparent
diff --git a/components/password_manager/content/common/credential_manager.typemap b/components/password_manager/content/common/credential_manager.typemap
index 571ba26..b3f4a5a 100644
--- a/components/password_manager/content/common/credential_manager.typemap
+++ b/components/password_manager/content/common/credential_manager.typemap
@@ -2,7 +2,7 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-mojom = "//components/password_manager/content/common/credential_manager.mojom"
+mojom = "//third_party/WebKit/public/platform/modules/credentialmanager/credential_manager.mojom"
 public_headers =
     [ "//components/password_manager/core/common/credential_manager_types.h" ]
 traits_headers = [ "//components/password_manager/content/common/credential_manager_struct_traits.h" ]
diff --git a/components/password_manager/content/common/credential_manager_struct_traits.h b/components/password_manager/content/common/credential_manager_struct_traits.h
index aa544b7..1fffa0d6 100644
--- a/components/password_manager/content/common/credential_manager_struct_traits.h
+++ b/components/password_manager/content/common/credential_manager_struct_traits.h
@@ -6,9 +6,9 @@
 #define COMPONENTS_PASSWORD_MANAGER_CONTENT_COMMON_CREDENTIAL_MANAGER_STRUCT_TRAITS_H_
 
 #include "base/strings/string16.h"
-#include "components/password_manager/content/common/credential_manager.mojom.h"
 #include "components/password_manager/core/common/credential_manager_types.h"
 #include "mojo/public/cpp/bindings/struct_traits.h"
+#include "third_party/WebKit/public/platform/modules/credentialmanager/credential_manager.mojom.h"
 
 namespace mojo {
 
diff --git a/components/password_manager/content/renderer/BUILD.gn b/components/password_manager/content/renderer/BUILD.gn
index 15db432..0d43743 100644
--- a/components/password_manager/content/renderer/BUILD.gn
+++ b/components/password_manager/content/renderer/BUILD.gn
@@ -10,7 +10,6 @@
 
   deps = [
     "//base",
-    "//components/password_manager/content/common:mojo_interfaces",
     "//components/password_manager/core/common",
     "//components/strings",
     "//content/public/common",
diff --git a/components/password_manager/content/renderer/credential_manager_client.h b/components/password_manager/content/renderer/credential_manager_client.h
index 513b389e..918e35e 100644
--- a/components/password_manager/content/renderer/credential_manager_client.h
+++ b/components/password_manager/content/renderer/credential_manager_client.h
@@ -7,12 +7,12 @@
 
 #include "base/compiler_specific.h"
 #include "base/macros.h"
-#include "components/password_manager/content/common/credential_manager.mojom.h"
 #include "content/public/renderer/render_view_observer.h"
 #include "third_party/WebKit/public/platform/WebCredentialManagerClient.h"
 #include "third_party/WebKit/public/platform/WebCredentialManagerError.h"
 #include "third_party/WebKit/public/platform/WebCredentialMediationRequirement.h"
 #include "third_party/WebKit/public/platform/WebVector.h"
+#include "third_party/WebKit/public/platform/modules/credentialmanager/credential_manager.mojom.h"
 
 namespace blink {
 class WebCredential;
diff --git a/components/pdf/DEPS b/components/pdf/DEPS
index d0c3033..ffff8fe 100644
--- a/components/pdf/DEPS
+++ b/components/pdf/DEPS
@@ -5,4 +5,7 @@
   "+third_party/skia/include",
   "+third_party/WebKit/public",
   "+ui/base",
+  "+ui/gfx",
+  "+ui/strings",
+  "+ui/touch_selection",
 ]
diff --git a/components/pdf/browser/BUILD.gn b/components/pdf/browser/BUILD.gn
index 30fff96ea..f7db28b 100644
--- a/components/pdf/browser/BUILD.gn
+++ b/components/pdf/browser/BUILD.gn
@@ -15,6 +15,7 @@
     "//base",
     "//components/pdf/common:interfaces",
     "//content/public/browser",
+    "//ui/touch_selection",
   ]
 
   public_deps = [
diff --git a/components/pdf/browser/pdf_web_contents_helper.cc b/components/pdf/browser/pdf_web_contents_helper.cc
index 64b814b..25066d2 100644
--- a/components/pdf/browser/pdf_web_contents_helper.cc
+++ b/components/pdf/browser/pdf_web_contents_helper.cc
@@ -10,6 +10,9 @@
 #include "base/memory/ptr_util.h"
 #include "base/strings/utf_string_conversions.h"
 #include "components/pdf/browser/pdf_web_contents_helper_client.h"
+#include "content/public/browser/render_widget_host_view.h"
+#include "ui/gfx/geometry/point_f.h"
+#include "ui/strings/grit/ui_strings.h"
 
 DEFINE_WEB_CONTENTS_USER_DATA_KEY(pdf::PDFWebContentsHelper);
 
@@ -31,9 +34,113 @@
     std::unique_ptr<PDFWebContentsHelperClient> client)
     : content::WebContentsObserver(web_contents),
       pdf_service_bindings_(web_contents, this),
-      client_(std::move(client)) {}
+      client_(std::move(client)),
+      touch_selection_controller_client_manager_(nullptr),
+      has_selection_(false) {}
 
 PDFWebContentsHelper::~PDFWebContentsHelper() {
+  if (!touch_selection_controller_client_manager_)
+    return;
+
+  touch_selection_controller_client_manager_->InvalidateClient(this);
+  touch_selection_controller_client_manager_->RemoveObserver(this);
+}
+
+void PDFWebContentsHelper::SelectionChanged(const gfx::Point& left,
+                                            int32_t left_height,
+                                            const gfx::Point& right,
+                                            int32_t right_height) {
+  if (!touch_selection_controller_client_manager_)
+    InitTouchSelectionClientManager();
+
+  if (touch_selection_controller_client_manager_) {
+    gfx::SelectionBound start;
+    gfx::SelectionBound end;
+    start.SetEdgeTop(gfx::PointF(left.x(), left.y()));
+    start.SetEdgeBottom(gfx::PointF(left.x(), left.y() + left_height));
+    start.set_type(gfx::SelectionBound::LEFT);
+    start.set_visible(true);
+    end.SetEdgeTop(gfx::PointF(right.x(), right.y()));
+    end.SetEdgeBottom(gfx::PointF(right.x(), right.y() + right_height));
+    end.set_type(gfx::SelectionBound::RIGHT);
+    end.set_visible(true);
+
+    has_selection_ = start != end;
+
+    touch_selection_controller_client_manager_->UpdateClientSelectionBounds(
+        start, end, this, this);
+  }
+}
+
+bool PDFWebContentsHelper::SupportsAnimation() const {
+  return false;
+}
+
+void PDFWebContentsHelper::MoveCaret(const gfx::PointF& position) {
+  // TODO(wjmaclean, dsinclair): Implement connection to PDFium to implement.
+}
+
+void PDFWebContentsHelper::MoveRangeSelectionExtent(const gfx::PointF& extent) {
+  // TODO(wjmaclean, dsinclair): Implement connection to PDFium to implement.
+}
+
+void PDFWebContentsHelper::SelectBetweenCoordinates(const gfx::PointF& base,
+                                                    const gfx::PointF& extent) {
+  // TODO(wjmaclean, dsinclair): Implement connection to PDFium to implement.
+}
+
+void PDFWebContentsHelper::OnSelectionEvent(ui::SelectionEventType event) {}
+
+std::unique_ptr<ui::TouchHandleDrawable>
+PDFWebContentsHelper::CreateDrawable() {
+  // We can return null here, as the manager will look after this.
+  return std::unique_ptr<ui::TouchHandleDrawable>();
+}
+
+void PDFWebContentsHelper::OnManagerWillDestroy(
+    content::TouchSelectionControllerClientManager* manager) {
+  DCHECK(manager == touch_selection_controller_client_manager_);
+  manager->RemoveObserver(this);
+  touch_selection_controller_client_manager_ = nullptr;
+}
+
+bool PDFWebContentsHelper::IsCommandIdEnabled(int command_id) const {
+  // TODO(wjmaclean|dsinclair): Make PDFium send readability information in the
+  // selection changed message?
+  bool readable = true;
+
+  switch (command_id) {
+    case IDS_APP_COPY:
+      return readable && has_selection_;
+      // TODO(wjmaclean): add logic for copy/paste as the information required
+      // from PDFium becomes available.
+  }
+  return false;
+}
+
+void PDFWebContentsHelper::ExecuteCommand(int command_id, int event_flags) {
+  // TODO(wjmaclean, dsinclair): Need to communicate to PDFium to get it to copy
+  // the selection onto the clipboard (and eventually accept cut/paste commands
+  // too).
+}
+
+void PDFWebContentsHelper::RunContextMenu() {
+  // TouchSelectionControllerClientAura will handle this for us.
+  NOTIMPLEMENTED();
+}
+
+void PDFWebContentsHelper::InitTouchSelectionClientManager() {
+  content::RenderWidgetHostView* view =
+      web_contents()->GetRenderWidgetHostView();
+  if (!view)
+    return;
+
+  touch_selection_controller_client_manager_ =
+      view->GetTouchSelectionControllerClientManager();
+  if (!touch_selection_controller_client_manager_)
+    return;
+
+  touch_selection_controller_client_manager_->AddObserver(this);
 }
 
 void PDFWebContentsHelper::HasUnsupportedFeature() {
diff --git a/components/pdf/browser/pdf_web_contents_helper.h b/components/pdf/browser/pdf_web_contents_helper.h
index 0d848c7..e91790f3 100644
--- a/components/pdf/browser/pdf_web_contents_helper.h
+++ b/components/pdf/browser/pdf_web_contents_helper.h
@@ -11,9 +11,13 @@
 #include "base/callback.h"
 #include "base/macros.h"
 #include "components/pdf/common/pdf.mojom.h"
+#include "content/public/browser/touch_selection_controller_client_manager.h"
 #include "content/public/browser/web_contents_binding_set.h"
 #include "content/public/browser/web_contents_observer.h"
 #include "content/public/browser/web_contents_user_data.h"
+#include "ui/touch_selection/selection_event_type.h"
+#include "ui/touch_selection/touch_selection_controller.h"
+#include "ui/touch_selection/touch_selection_menu_runner.h"
 
 namespace content {
 class WebContents;
@@ -27,7 +31,10 @@
 class PDFWebContentsHelper
     : public content::WebContentsObserver,
       public content::WebContentsUserData<PDFWebContentsHelper>,
-      public mojom::PdfService {
+      public mojom::PdfService,
+      public ui::TouchSelectionControllerClient,
+      public ui::TouchSelectionMenuClient,
+      public content::TouchSelectionControllerClientManager::Observer {
  public:
   ~PDFWebContentsHelper() override;
 
@@ -35,10 +42,36 @@
       content::WebContents* contents,
       std::unique_ptr<PDFWebContentsHelperClient> client);
 
+  void SelectionChanged(const gfx::Point& left,
+                        int32_t left_height,
+                        const gfx::Point& right,
+                        int32_t right_height);
+
+  // ui::TouchSelectionControllerClient :
+  bool SupportsAnimation() const override;
+  void SetNeedsAnimate() override {}
+  void MoveCaret(const gfx::PointF& position) override;
+  void MoveRangeSelectionExtent(const gfx::PointF& extent) override;
+  void SelectBetweenCoordinates(const gfx::PointF& base,
+                                const gfx::PointF& extent) override;
+  void OnSelectionEvent(ui::SelectionEventType event) override;
+  std::unique_ptr<ui::TouchHandleDrawable> CreateDrawable() override;
+
+  // ui::TouchSelectionMenuRunner:
+  bool IsCommandIdEnabled(int command_id) const override;
+  void ExecuteCommand(int command_id, int event_flags) override;
+  void RunContextMenu() override;
+
+  // ui::TouchSelectionControllerClientManager::Observer:
+  void OnManagerWillDestroy(
+      content::TouchSelectionControllerClientManager* manager) override;
+
  private:
   PDFWebContentsHelper(content::WebContents* web_contents,
                        std::unique_ptr<PDFWebContentsHelperClient> client);
 
+  void InitTouchSelectionClientManager();
+
   // mojom::PdfService:
   void HasUnsupportedFeature() override;
   void SaveUrlAs(const GURL& url, const content::Referrer& referrer) override;
@@ -46,6 +79,9 @@
 
   content::WebContentsFrameBindingSet<mojom::PdfService> pdf_service_bindings_;
   std::unique_ptr<PDFWebContentsHelperClient> client_;
+  content::TouchSelectionControllerClientManager*
+      touch_selection_controller_client_manager_;
+  bool has_selection_;
 
   DISALLOW_COPY_AND_ASSIGN(PDFWebContentsHelper);
 };
diff --git a/components/safe_browsing/common/BUILD.gn b/components/safe_browsing/common/BUILD.gn
index eb5b5f9..61bc49b 100644
--- a/components/safe_browsing/common/BUILD.gn
+++ b/components/safe_browsing/common/BUILD.gn
@@ -33,6 +33,7 @@
   ]
   deps = [
     "//base:base",
+    "//components/pref_registry:pref_registry",
     "//components/prefs",
   ]
 }
diff --git a/components/safe_browsing/common/DEPS b/components/safe_browsing/common/DEPS
index c908d82..60d09aae 100644
--- a/components/safe_browsing/common/DEPS
+++ b/components/safe_browsing/common/DEPS
@@ -1,4 +1,5 @@
 include_rules = [
+  "+components/pref_registry",
   "+components/prefs",
   "+crypto/sha2.h",
   "+ipc",
diff --git a/components/safe_browsing/common/safe_browsing_prefs.cc b/components/safe_browsing/common/safe_browsing_prefs.cc
index 5a97bfc..20aa6b6 100644
--- a/components/safe_browsing/common/safe_browsing_prefs.cc
+++ b/components/safe_browsing/common/safe_browsing_prefs.cc
@@ -7,6 +7,7 @@
 #include "base/command_line.h"
 #include "base/logging.h"
 #include "base/metrics/histogram_macros.h"
+#include "components/pref_registry/pref_registry_syncable.h"
 #include "components/prefs/pref_registry_simple.h"
 #include "components/prefs/pref_service.h"
 
@@ -140,18 +141,22 @@
 }  // namespace
 
 namespace prefs {
-const char kSafeBrowsingExtendedReportingOptInAllowed[] =
-    "safebrowsing.extended_reporting_opt_in_allowed";
+const char kSafeBrowsingEnabled[] = "safebrowsing.enabled";
 const char kSafeBrowsingExtendedReportingEnabled[] =
     "safebrowsing.extended_reporting_enabled";
-const char kSafeBrowsingScoutReportingEnabled[] =
-    "safebrowsing.scout_reporting_enabled";
-const char kSafeBrowsingScoutGroupSelected[] =
-    "safebrowsing.scout_group_selected";
+const char kSafeBrowsingExtendedReportingOptInAllowed[] =
+    "safebrowsing.extended_reporting_opt_in_allowed";
+const char kSafeBrowsingIncidentsSent[] = "safebrowsing.incidents_sent";
+const char kSafeBrowsingProceedAnywayDisabled[] =
+    "safebrowsing.proceed_anyway_disabled";
 const char kSafeBrowsingSawInterstitialExtendedReporting[] =
     "safebrowsing.saw_interstitial_sber1";
 const char kSafeBrowsingSawInterstitialScoutReporting[] =
     "safebrowsing.saw_interstitial_sber2";
+const char kSafeBrowsingScoutGroupSelected[] =
+    "safebrowsing.scout_group_selected";
+const char kSafeBrowsingScoutReportingEnabled[] =
+    "safebrowsing.scout_reporting_enabled";
 }  // namespace prefs
 
 namespace safe_browsing {
@@ -353,7 +358,6 @@
 }
 
 void RegisterProfilePrefs(PrefRegistrySimple* registry) {
-  // TODO(lpz): Move other safe browsing prefs here from c/b/profiles/profile.cc
   registry->RegisterBooleanPref(prefs::kSafeBrowsingExtendedReportingEnabled,
                                 false);
   registry->RegisterBooleanPref(prefs::kSafeBrowsingScoutReportingEnabled,
@@ -365,6 +369,12 @@
       prefs::kSafeBrowsingSawInterstitialScoutReporting, false);
   registry->RegisterBooleanPref(
       prefs::kSafeBrowsingExtendedReportingOptInAllowed, true);
+  registry->RegisterBooleanPref(
+      prefs::kSafeBrowsingEnabled, true,
+      user_prefs::PrefRegistrySyncable::SYNCABLE_PREF);
+  registry->RegisterBooleanPref(prefs::kSafeBrowsingProceedAnywayDisabled,
+                                false);
+  registry->RegisterDictionaryPref(prefs::kSafeBrowsingIncidentsSent);
 }
 
 void SetExtendedReportingPrefAndMetric(
diff --git a/components/safe_browsing/common/safe_browsing_prefs.h b/components/safe_browsing/common/safe_browsing_prefs.h
index 5923d9c..fe70b6d 100644
--- a/components/safe_browsing/common/safe_browsing_prefs.h
+++ b/components/safe_browsing/common/safe_browsing_prefs.h
@@ -13,22 +13,23 @@
 class PrefService;
 
 namespace prefs {
+// Boolean that is true when SafeBrowsing is enabled.
+extern const char kSafeBrowsingEnabled[];
+
+// Boolean that tell us whether Safe Browsing extended reporting is enabled.
+extern const char kSafeBrowsingExtendedReportingEnabled[];
+
 // Boolean that tells us whether users are given the option to opt in to Safe
 // Browsing extended reporting. This is exposed as a preference that can be
 // overridden by enterprise policy.
 extern const char kSafeBrowsingExtendedReportingOptInAllowed[];
 
-// Boolean that tell us whether Safe Browsing extended reporting is enabled.
-extern const char kSafeBrowsingExtendedReportingEnabled[];
+// A dictionary mapping incident types to a dict of incident key:digest pairs.
+extern const char kSafeBrowsingIncidentsSent[];
 
-// Boolean indicating whether Safe Browsing Scout reporting is enabled, which
-// collects data for malware detection.
-extern const char kSafeBrowsingScoutReportingEnabled[];
-
-// Boolean indicating whether the Scout reporting workflow is enabled. This
-// affects which of SafeBrowsingExtendedReporting or SafeBrowsingScoutReporting
-// is used.
-extern const char kSafeBrowsingScoutGroupSelected[];
+// Boolean that is true when the SafeBrowsing interstitial should not allow
+// users to proceed anyway.
+extern const char kSafeBrowsingProceedAnywayDisabled[];
 
 // Boolean indicating whether the user has ever seen a security interstitial
 // containing the legacy Extended Reporting opt-in.
@@ -37,6 +38,15 @@
 // Boolean indicating whether the user has ever seen a security interstitial
 // containing the new Scout opt-in.
 extern const char kSafeBrowsingSawInterstitialScoutReporting[];
+
+// Boolean indicating whether the Scout reporting workflow is enabled. This
+// affects which of SafeBrowsingExtendedReporting or SafeBrowsingScoutReporting
+// is used.
+extern const char kSafeBrowsingScoutGroupSelected[];
+
+// Boolean indicating whether Safe Browsing Scout reporting is enabled, which
+// collects data for malware detection.
+extern const char kSafeBrowsingScoutReportingEnabled[];
 }
 
 namespace safe_browsing {
diff --git a/components/signin/core/browser/android/java/src/org/chromium/components/signin/AccountManagerDelegate.java b/components/signin/core/browser/android/java/src/org/chromium/components/signin/AccountManagerDelegate.java
index 15666713..07018d3 100644
--- a/components/signin/core/browser/android/java/src/org/chromium/components/signin/AccountManagerDelegate.java
+++ b/components/signin/core/browser/android/java/src/org/chromium/components/signin/AccountManagerDelegate.java
@@ -34,14 +34,6 @@
     void removeObserver(AccountsChangeObserver observer);
 
     /**
-     * Get all the accounts for a given {@code type}.
-     * This is deprecated an will be removed soon.
-     */
-    @Deprecated
-    @WorkerThread
-    Account[] getAccountsByType(String type);
-
-    /**
      * Get all the accounts.
      * This method shouldn't be called on the UI thread (violated due to crbug.com/517697).
      */
diff --git a/components/signin/core/browser/android/java/src/org/chromium/components/signin/AccountManagerHelper.java b/components/signin/core/browser/android/java/src/org/chromium/components/signin/AccountManagerHelper.java
index 9692855..5dbc00da 100644
--- a/components/signin/core/browser/android/java/src/org/chromium/components/signin/AccountManagerHelper.java
+++ b/components/signin/core/browser/android/java/src/org/chromium/components/signin/AccountManagerHelper.java
@@ -248,15 +248,6 @@
     }
 
     /**
-     * This method is deprecated and will be removed soon.
-     */
-    // TODO(bsazonov) remove this method after fixing internal usages.
-    @MainThread
-    public void getGoogleAccounts(final Callback<Account[]> callback) {
-        tryGetGoogleAccounts(callback);
-    }
-
-    /**
      * Determine whether there are any Google accounts on the device.
      * Returns false if an error occurs while getting account list.
      */
diff --git a/components/signin/core/browser/android/java/src/org/chromium/components/signin/SystemAccountManagerDelegate.java b/components/signin/core/browser/android/java/src/org/chromium/components/signin/SystemAccountManagerDelegate.java
index 9bc930ab..784ec7c 100644
--- a/components/signin/core/browser/android/java/src/org/chromium/components/signin/SystemAccountManagerDelegate.java
+++ b/components/signin/core/browser/android/java/src/org/chromium/components/signin/SystemAccountManagerDelegate.java
@@ -77,12 +77,12 @@
     }
 
     @Override
-    public Account[] getAccountsByType(String type) {
+    public Account[] getAccountsSync() throws AccountManagerDelegateException {
         if (!hasGetAccountsPermission()) {
             return new Account[] {};
         }
         long now = SystemClock.elapsedRealtime();
-        Account[] accounts = mAccountManager.getAccountsByType(type);
+        Account[] accounts = mAccountManager.getAccountsByType(GoogleAuthUtil.GOOGLE_ACCOUNT_TYPE);
         long elapsed = SystemClock.elapsedRealtime() - now;
         recordElapsedTimeHistogram("Signin.AndroidGetAccountsTime_AccountManager", elapsed);
         if (ThreadUtils.runningOnUiThread()) {
@@ -93,11 +93,6 @@
     }
 
     @Override
-    public Account[] getAccountsSync() throws AccountManagerDelegateException {
-        return getAccountsByType(GoogleAuthUtil.GOOGLE_ACCOUNT_TYPE);
-    }
-
-    @Override
     public String getAuthToken(Account account, String authTokenScope) throws AuthException {
         assert !ThreadUtils.runningOnUiThread();
         assert AccountManagerHelper.GOOGLE_ACCOUNT_TYPE.equals(account.type);
diff --git a/components/signin/core/browser/android/javatests/src/org/chromium/components/signin/test/util/FakeAccountManagerDelegate.java b/components/signin/core/browser/android/javatests/src/org/chromium/components/signin/test/util/FakeAccountManagerDelegate.java
index 804d3b4..7c42a0f1 100644
--- a/components/signin/core/browser/android/javatests/src/org/chromium/components/signin/test/util/FakeAccountManagerDelegate.java
+++ b/components/signin/core/browser/android/javatests/src/org/chromium/components/signin/test/util/FakeAccountManagerDelegate.java
@@ -69,20 +69,6 @@
     }
 
     @Override
-    public Account[] getAccountsByType(String type) {
-        if (!AccountManagerHelper.GOOGLE_ACCOUNT_TYPE.equals(type)) {
-            throw new IllegalArgumentException("Invalid account type: " + type);
-        }
-        ArrayList<Account> validAccounts = new ArrayList<>();
-        for (AccountHolder ah : mAccounts) {
-            if (type.equals(ah.getAccount().type)) {
-                validAccounts.add(ah.getAccount());
-            }
-        }
-        return validAccounts.toArray(new Account[0]);
-    }
-
-    @Override
     public Account[] getAccountsSync() throws AccountManagerDelegateException {
         return getAccountsSyncNoThrow();
     }
diff --git a/components/signin/core/browser/profile_oauth2_token_service.cc b/components/signin/core/browser/profile_oauth2_token_service.cc
index 7e8eb2a..8cf2fd9 100644
--- a/components/signin/core/browser/profile_oauth2_token_service.cc
+++ b/components/signin/core/browser/profile_oauth2_token_service.cc
@@ -6,7 +6,7 @@
 
 ProfileOAuth2TokenService::ProfileOAuth2TokenService(
     std::unique_ptr<OAuth2TokenServiceDelegate> delegate)
-    : OAuth2TokenService(std::move(delegate)) {
+    : OAuth2TokenService(std::move(delegate)), all_credentials_loaded_(false) {
   AddObserver(this);
 }
 
@@ -24,6 +24,10 @@
   GetDelegate()->LoadCredentials(primary_account_id);
 }
 
+bool ProfileOAuth2TokenService::AreAllCredentialsLoaded() {
+  return all_credentials_loaded_;
+}
+
 void ProfileOAuth2TokenService::UpdateCredentials(
     const std::string& account_id,
     const std::string& refresh_token) {
@@ -50,3 +54,7 @@
   CancelRequestsForAccount(account_id);
   ClearCacheForAccount(account_id);
 }
+
+void ProfileOAuth2TokenService::OnRefreshTokensLoaded() {
+  all_credentials_loaded_ = true;
+}
diff --git a/components/signin/core/browser/profile_oauth2_token_service.h b/components/signin/core/browser/profile_oauth2_token_service.h
index b7beb4ee..3372a92 100644
--- a/components/signin/core/browser/profile_oauth2_token_service.h
+++ b/components/signin/core/browser/profile_oauth2_token_service.h
@@ -45,7 +45,10 @@
   // The primary account is specified with the |primary_account_id| argument.
   // For a regular profile, the primary account id comes from SigninManager.
   // For a supervised user, the id comes from SupervisedUserService.
-  virtual void LoadCredentials(const std::string& primary_account_id);
+  void LoadCredentials(const std::string& primary_account_id);
+
+  // Returns true iff all credentials have been loaded from disk.
+  bool AreAllCredentialsLoaded();
 
   // Updates a |refresh_token| for an |account_id|. Credentials are persisted,
   // and available through |LoadCredentials| after service is restarted.
@@ -61,6 +64,10 @@
  private:
   void OnRefreshTokenAvailable(const std::string& account_id) override;
   void OnRefreshTokenRevoked(const std::string& account_id) override;
+  void OnRefreshTokensLoaded() override;
+
+  // Whether all credentials have been loaded.
+  bool all_credentials_loaded_;
 
   DISALLOW_COPY_AND_ASSIGN(ProfileOAuth2TokenService);
 };
diff --git a/components/sync/BUILD.gn b/components/sync/BUILD.gn
index 6bd0d7f..d3402fa1 100644
--- a/components/sync/BUILD.gn
+++ b/components/sync/BUILD.gn
@@ -1024,30 +1024,20 @@
 static_library("test_support_fake_server") {
   testonly = true
   sources = [
-    "test/fake_server/bookmark_entity.cc",
-    "test/fake_server/bookmark_entity.h",
     "test/fake_server/bookmark_entity_builder.cc",
     "test/fake_server/bookmark_entity_builder.h",
     "test/fake_server/entity_builder_factory.cc",
     "test/fake_server/entity_builder_factory.h",
     "test/fake_server/fake_server.cc",
     "test/fake_server/fake_server.h",
-    "test/fake_server/fake_server_entity.cc",
-    "test/fake_server/fake_server_entity.h",
     "test/fake_server/fake_server_http_post_provider.cc",
     "test/fake_server/fake_server_http_post_provider.h",
     "test/fake_server/fake_server_network_resources.cc",
     "test/fake_server/fake_server_network_resources.h",
     "test/fake_server/fake_server_verifier.cc",
     "test/fake_server/fake_server_verifier.h",
-    "test/fake_server/permanent_entity.cc",
-    "test/fake_server/permanent_entity.h",
     "test/fake_server/sessions_hierarchy.cc",
     "test/fake_server/sessions_hierarchy.h",
-    "test/fake_server/tombstone_entity.cc",
-    "test/fake_server/tombstone_entity.h",
-    "test/fake_server/unique_client_entity.cc",
-    "test/fake_server/unique_client_entity.h",
   ]
 
   deps = [
diff --git a/components/sync/engine_impl/loopback_server/loopback_server.cc b/components/sync/engine_impl/loopback_server/loopback_server.cc
index 61094cf..2a4be37 100644
--- a/components/sync/engine_impl/loopback_server/loopback_server.cc
+++ b/components/sync/engine_impl/loopback_server/loopback_server.cc
@@ -134,7 +134,10 @@
 }  // namespace
 
 LoopbackServer::LoopbackServer(const base::FilePath& persistent_file)
-    : version_(0), store_birthday_(0), persistent_file_(persistent_file) {
+    : version_(0),
+      store_birthday_(0),
+      persistent_file_(persistent_file),
+      observer_for_tests_(NULL) {
   Init();
 }
 
@@ -160,8 +163,9 @@
     const std::string& name) {
   DCHECK(thread_checker_.CalledOnValidThread());
   std::unique_ptr<LoopbackServerEntity> entity =
-      PersistentPermanentEntity::Create(syncer::BOOKMARKS, server_tag, name,
-                                        ModelTypeToRootTag(syncer::BOOKMARKS));
+      PersistentPermanentEntity::CreateNew(
+          syncer::BOOKMARKS, server_tag, name,
+          ModelTypeToRootTag(syncer::BOOKMARKS));
   if (!entity)
     return false;
 
@@ -323,19 +327,18 @@
   }
 
   std::unique_ptr<LoopbackServerEntity> entity;
+  syncer::ModelType type = GetModelType(client_entity);
   if (client_entity.deleted()) {
-    entity = PersistentTombstoneEntity::Create(client_entity);
+    entity = PersistentTombstoneEntity::CreateFromEntity(client_entity);
     DeleteChildren(client_entity.id_string());
-  } else if (GetModelType(client_entity) == syncer::NIGORI) {
+  } else if (type == syncer::NIGORI) {
     // NIGORI is the only permanent item type that should be updated by the
     // client.
     EntityMap::const_iterator iter = entities_.find(client_entity.id_string());
     CHECK(iter != entities_.end());
     entity = PersistentPermanentEntity::CreateUpdatedNigoriEntity(
         client_entity, *iter->second);
-  } else if (client_entity.has_client_defined_unique_tag()) {
-    entity = PersistentUniqueClientEntity::Create(client_entity);
-  } else {
+  } else if (type == syncer::BOOKMARKS) {
     // TODO(pvalenzuela): Validate entity's parent ID.
     EntityMap::const_iterator iter = entities_.find(client_entity.id_string());
     if (iter != entities_.end()) {
@@ -345,13 +348,8 @@
       entity = PersistentBookmarkEntity::CreateNew(client_entity, parent_id,
                                                    client_guid);
     }
-  }
-
-  if (!entity) {
-    LOG(ERROR) << "No server entity was created for client entity with type: "
-               << GetModelType(client_entity)
-               << " and ID: " << client_entity.id_string() << ".";
-    return string();
+  } else {
+    entity = PersistentUniqueClientEntity::CreateFromEntity(client_entity);
   }
 
   const std::string id = entity->GetId();
@@ -360,13 +358,20 @@
   return id;
 }
 
+void LoopbackServer::OverrideResponseType(
+    ResponseTypeProvider response_type_override) {
+  response_type_override_ = std::move(response_type_override);
+}
+
 void LoopbackServer::BuildEntryResponseForSuccessfulCommit(
     const std::string& entity_id,
     sync_pb::CommitResponse_EntryResponse* entry_response) {
   EntityMap::const_iterator iter = entities_.find(entity_id);
   CHECK(iter != entities_.end());
   const LoopbackServerEntity& entity = *iter->second;
-  entry_response->set_response_type(sync_pb::CommitResponse::SUCCESS);
+  entry_response->set_response_type(response_type_override_
+                                        ? response_type_override_.Run(entity)
+                                        : sync_pb::CommitResponse::SUCCESS);
   entry_response->set_id_string(entity.GetId());
 
   if (entity.IsDeleted()) {
@@ -406,7 +411,7 @@
   }
 
   for (auto& tombstone : tombstones) {
-    SaveEntity(PersistentTombstoneEntity::Create(tombstone));
+    SaveEntity(PersistentTombstoneEntity::CreateFromEntity(tombstone));
   }
 }
 
@@ -446,6 +451,9 @@
     committed_model_types.Put(iter->second->GetModelType());
   }
 
+  if (observer_for_tests_)
+    observer_for_tests_->OnCommit(invalidator_client_id, committed_model_types);
+
   return true;
 }
 
@@ -454,6 +462,7 @@
   entities_.clear();
   keystore_keys_.clear();
   ++store_birthday_;
+  base::DeleteFile(persistent_file_, false);
   Init();
 }
 
@@ -462,6 +471,98 @@
   return base::Int64ToString(store_birthday_);
 }
 
+std::vector<sync_pb::SyncEntity> LoopbackServer::GetSyncEntitiesByModelType(
+    ModelType model_type) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  std::vector<sync_pb::SyncEntity> sync_entities;
+  for (EntityMap::const_iterator it = entities_.begin(); it != entities_.end();
+       ++it) {
+    const LoopbackServerEntity& entity = *it->second;
+    if (!(entity.IsDeleted() || entity.IsPermanent()) &&
+        entity.GetModelType() == model_type) {
+      sync_pb::SyncEntity sync_entity;
+      entity.SerializeAsProto(&sync_entity);
+      sync_entities.push_back(sync_entity);
+    }
+  }
+  return sync_entities;
+}
+
+std::unique_ptr<base::DictionaryValue>
+LoopbackServer::GetEntitiesAsDictionaryValue() {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  std::unique_ptr<base::DictionaryValue> dictionary(
+      new base::DictionaryValue());
+
+  // Initialize an empty ListValue for all ModelTypes.
+  ModelTypeSet all_types = ModelTypeSet::All();
+  for (ModelTypeSet::Iterator it = all_types.First(); it.Good(); it.Inc()) {
+    dictionary->Set(ModelTypeToString(it.Get()),
+                    base::MakeUnique<base::ListValue>());
+  }
+
+  for (EntityMap::const_iterator it = entities_.begin(); it != entities_.end();
+       ++it) {
+    const LoopbackServerEntity& entity = *it->second;
+    if (entity.IsDeleted() || entity.IsPermanent()) {
+      // Tombstones are ignored as they don't represent current data. Folders
+      // are also ignored as current verification infrastructure does not
+      // consider them.
+      continue;
+    }
+    base::ListValue* list_value;
+    if (!dictionary->GetList(ModelTypeToString(entity.GetModelType()),
+                             &list_value)) {
+      return std::unique_ptr<base::DictionaryValue>();
+    }
+    // TODO(pvalenzuela): Store more data for each entity so additional
+    // verification can be performed. One example of additional verification
+    // is checking the correctness of the bookmark hierarchy.
+    list_value->AppendString(entity.GetName());
+  }
+
+  return dictionary;
+}
+
+bool LoopbackServer::ModifyEntitySpecifics(
+    const std::string& id,
+    const sync_pb::EntitySpecifics& updated_specifics) {
+  EntityMap::const_iterator iter = entities_.find(id);
+  if (iter == entities_.end() ||
+      iter->second->GetModelType() !=
+          GetModelTypeFromSpecifics(updated_specifics)) {
+    return false;
+  }
+
+  LoopbackServerEntity* entity = iter->second.get();
+  entity->SetSpecifics(updated_specifics);
+  UpdateEntityVersion(entity);
+  return true;
+}
+
+bool LoopbackServer::ModifyBookmarkEntity(
+    const std::string& id,
+    const std::string& parent_id,
+    const sync_pb::EntitySpecifics& updated_specifics) {
+  EntityMap::const_iterator iter = entities_.find(id);
+  if (iter == entities_.end() ||
+      iter->second->GetModelType() != syncer::BOOKMARKS ||
+      GetModelTypeFromSpecifics(updated_specifics) != syncer::BOOKMARKS) {
+    return false;
+  }
+
+  PersistentBookmarkEntity* entity =
+      static_cast<PersistentBookmarkEntity*>(iter->second.get());
+
+  entity->SetParentId(parent_id);
+  entity->SetSpecifics(updated_specifics);
+  if (updated_specifics.has_bookmark()) {
+    entity->SetName(updated_specifics.bookmark().title());
+  }
+  UpdateEntityVersion(entity);
+  return true;
+}
+
 void LoopbackServer::SerializeState(sync_pb::LoopbackServerProto* proto) const {
   DCHECK(thread_checker_.CalledOnValidThread());
 
diff --git a/components/sync/engine_impl/loopback_server/loopback_server.h b/components/sync/engine_impl/loopback_server/loopback_server.h
index a69c99e..0f5d5d06 100644
--- a/components/sync/engine_impl/loopback_server/loopback_server.h
+++ b/components/sync/engine_impl/loopback_server/loopback_server.h
@@ -22,11 +22,25 @@
 #include "components/sync/protocol/loopback_server.pb.h"
 #include "components/sync/protocol/sync.pb.h"
 
+namespace fake_server {
+class FakeServer;
+}
+
 namespace syncer {
 
 // A loopback version of the Sync server used for local profile serialization.
 class LoopbackServer {
  public:
+  class ObserverForTests {
+   public:
+    virtual ~ObserverForTests() {}
+
+    // Called after the server has processed a successful commit. The types
+    // updated as part of the commit are passed in |committed_model_types|.
+    virtual void OnCommit(const std::string& committer_id,
+                          syncer::ModelTypeSet committed_model_types) = 0;
+  };
+
   explicit LoopbackServer(const base::FilePath& persistent_file);
   virtual ~LoopbackServer();
 
@@ -40,9 +54,16 @@
                      std::string* response);
 
  private:
+  // Allow the FakeServer decorator to inspect the internals of this class.
+  friend class fake_server::FakeServer;
+
   using EntityMap =
       std::map<std::string, std::unique_ptr<LoopbackServerEntity>>;
 
+  using ResponseTypeProvider =
+      base::RepeatingCallback<sync_pb::CommitResponse::ResponseType(
+          const LoopbackServerEntity& entity)>;
+
   // Gets LoopbackServer ready for syncing.
   void Init();
 
@@ -102,6 +123,44 @@
   // Returns the store birthday.
   std::string GetStoreBirthday() const;
 
+  // Returns all entities stored by the server of the given |model_type|.
+  // This method is only used in tests.
+  std::vector<sync_pb::SyncEntity> GetSyncEntitiesByModelType(
+      syncer::ModelType model_type);
+
+  // Creates a DicionaryValue representation of all entities present in the
+  // server. The dictionary keys are the strings generated by ModelTypeToString
+  // and the values are ListValues containing StringValue versions of entity
+  // names. Used by test to verify the contents of the server state.
+  std::unique_ptr<base::DictionaryValue> GetEntitiesAsDictionaryValue();
+
+  // Modifies the entity on the server with the given |id|. The entity's
+  // EntitySpecifics are replaced with |updated_specifics| and its version is
+  // updated to n+1. If the given |id| does not exist or the ModelType of
+  // |updated_specifics| does not match the entity, false is returned.
+  // Otherwise, true is returned to represent a successful modification.
+  //
+  // This method sometimes updates entity data beyond EntitySpecifics. For
+  // example, in the case of a bookmark, changing the BookmarkSpecifics title
+  // field will modify the top-level entity's name field.
+  // This method is only used in tests.
+  bool ModifyEntitySpecifics(const std::string& id,
+                             const sync_pb::EntitySpecifics& updated_specifics);
+
+  // This method is only used in tests.
+  bool ModifyBookmarkEntity(const std::string& id,
+                            const std::string& parent_id,
+                            const sync_pb::EntitySpecifics& updated_specifics);
+
+  // Use this callback to generate response types for entities. They will still
+  // be "committed" and stored as normal, this only affects the response type
+  // the client sees. This allows tests to still inspect what the client has
+  // done, although not as useful of a mechanism for multi client tests. Care
+  // should be taken when failing responses, as the client will go into
+  // exponential backoff, which can cause tests to be slow or time out.
+  // This method is only used in tests.
+  void OverrideResponseType(ResponseTypeProvider response_type_override);
+
   // Serializes the server state to |proto|.
   void SerializeState(sync_pb::LoopbackServerProto* proto) const;
 
@@ -114,6 +173,10 @@
   // Loads all entities and server state from a protobuf file in |filename|.
   bool LoadStateFromFile(const base::FilePath& filename);
 
+  void set_observer_for_tests(ObserverForTests* observer) {
+    observer_for_tests_ = observer;
+  }
+
   // This is the last version number assigned to an entity. The next entity will
   // have a version number of version_ + 1.
   int64_t version_;
@@ -128,6 +191,12 @@
 
   // Used to verify that LoopbackServer is only used from one thread.
   base::ThreadChecker thread_checker_;
+
+  // Used to observe the completion of commit messages for the sake of testing.
+  ObserverForTests* observer_for_tests_;
+
+  // Response type override callback used in tests.
+  ResponseTypeProvider response_type_override_;
 };
 
 }  // namespace syncer
diff --git a/components/sync/engine_impl/loopback_server/loopback_server_entity.cc b/components/sync/engine_impl/loopback_server/loopback_server_entity.cc
index c6e20fe..946fd940 100644
--- a/components/sync/engine_impl/loopback_server/loopback_server_entity.cc
+++ b/components/sync/engine_impl/loopback_server/loopback_server_entity.cc
@@ -46,7 +46,7 @@
     const sync_pb::LoopbackServerEntity& entity) {
   switch (entity.type()) {
     case sync_pb::LoopbackServerEntity_Type_TOMBSTONE:
-      return PersistentTombstoneEntity::Create(entity.entity());
+      return PersistentTombstoneEntity::CreateFromEntity(entity.entity());
     case sync_pb::LoopbackServerEntity_Type_PERMANENT:
       return base::MakeUnique<PersistentPermanentEntity>(
           entity.entity().id_string(), entity.entity().version(),
@@ -57,7 +57,7 @@
     case sync_pb::LoopbackServerEntity_Type_BOOKMARK:
       return PersistentBookmarkEntity::CreateFromEntity(entity.entity());
     case sync_pb::LoopbackServerEntity_Type_UNIQUE:
-      return PersistentUniqueClientEntity::Create(entity.entity());
+      return PersistentUniqueClientEntity::CreateFromEntity(entity.entity());
     case sync_pb::LoopbackServerEntity_Type_UNKNOWN:
       CHECK(false) << "Unknown type encountered";
       return std::unique_ptr<LoopbackServerEntity>(nullptr);
diff --git a/components/sync/engine_impl/loopback_server/persistent_permanent_entity.cc b/components/sync/engine_impl/loopback_server/persistent_permanent_entity.cc
index e85aa022..48d25cfe 100644
--- a/components/sync/engine_impl/loopback_server/persistent_permanent_entity.cc
+++ b/components/sync/engine_impl/loopback_server/persistent_permanent_entity.cc
@@ -22,7 +22,7 @@
 PersistentPermanentEntity::~PersistentPermanentEntity() {}
 
 // static
-std::unique_ptr<LoopbackServerEntity> PersistentPermanentEntity::Create(
+std::unique_ptr<LoopbackServerEntity> PersistentPermanentEntity::CreateNew(
     const ModelType& model_type,
     const string& server_tag,
     const string& name,
diff --git a/components/sync/engine_impl/loopback_server/persistent_permanent_entity.h b/components/sync/engine_impl/loopback_server/persistent_permanent_entity.h
index 2011235..3a1d08a 100644
--- a/components/sync/engine_impl/loopback_server/persistent_permanent_entity.h
+++ b/components/sync/engine_impl/loopback_server/persistent_permanent_entity.h
@@ -29,7 +29,7 @@
 
   // Factory function for PersistentPermanentEntity. |server_tag| should be a
   // globally unique identifier.
-  static std::unique_ptr<LoopbackServerEntity> Create(
+  static std::unique_ptr<LoopbackServerEntity> CreateNew(
       const syncer::ModelType& model_type,
       const std::string& server_tag,
       const std::string& name,
diff --git a/components/sync/engine_impl/loopback_server/persistent_tombstone_entity.cc b/components/sync/engine_impl/loopback_server/persistent_tombstone_entity.cc
index a32b134c..f1e3c5a 100644
--- a/components/sync/engine_impl/loopback_server/persistent_tombstone_entity.cc
+++ b/components/sync/engine_impl/loopback_server/persistent_tombstone_entity.cc
@@ -13,20 +13,33 @@
 PersistentTombstoneEntity::~PersistentTombstoneEntity() {}
 
 // static
-std::unique_ptr<LoopbackServerEntity> PersistentTombstoneEntity::Create(
-    const sync_pb::SyncEntity& entity) {
+std::unique_ptr<LoopbackServerEntity>
+PersistentTombstoneEntity::CreateFromEntity(const sync_pb::SyncEntity& entity) {
   const ModelType model_type = GetModelTypeFromId(entity.id_string());
   CHECK_NE(model_type, syncer::UNSPECIFIED) << "Invalid ID was given: "
                                             << entity.id_string();
   return std::unique_ptr<LoopbackServerEntity>(new PersistentTombstoneEntity(
-      entity.id_string(), entity.version(), model_type));
+      entity.id_string(), entity.version(), model_type,
+      entity.client_defined_unique_tag()));
+}
+
+// static
+std::unique_ptr<LoopbackServerEntity> PersistentTombstoneEntity::CreateNew(
+    const std::string& id,
+    const std::string& client_defined_unique_tag) {
+  const ModelType model_type = GetModelTypeFromId(id);
+  CHECK_NE(model_type, syncer::UNSPECIFIED) << "Invalid ID was given: " << id;
+  return std::unique_ptr<LoopbackServerEntity>(new PersistentTombstoneEntity(
+      id, 0, model_type, client_defined_unique_tag));
 }
 
 PersistentTombstoneEntity::PersistentTombstoneEntity(
     const string& id,
     int64_t version,
-    const ModelType& model_type)
-    : LoopbackServerEntity(id, model_type, version, string()) {
+    const ModelType& model_type,
+    const std::string& client_defined_unique_tag)
+    : LoopbackServerEntity(id, model_type, version, string()),
+      client_defined_unique_tag_(client_defined_unique_tag) {
   sync_pb::EntitySpecifics specifics;
   AddDefaultFieldValue(model_type, &specifics);
   SetSpecifics(specifics);
@@ -43,6 +56,8 @@
 void PersistentTombstoneEntity::SerializeAsProto(
     sync_pb::SyncEntity* proto) const {
   LoopbackServerEntity::SerializeBaseProtoFields(proto);
+  if (!client_defined_unique_tag_.empty())
+    proto->set_client_defined_unique_tag(client_defined_unique_tag_);
 }
 
 bool PersistentTombstoneEntity::IsDeleted() const {
diff --git a/components/sync/engine_impl/loopback_server/persistent_tombstone_entity.h b/components/sync/engine_impl/loopback_server/persistent_tombstone_entity.h
index ea7236d..0531666 100644
--- a/components/sync/engine_impl/loopback_server/persistent_tombstone_entity.h
+++ b/components/sync/engine_impl/loopback_server/persistent_tombstone_entity.h
@@ -20,9 +20,13 @@
   ~PersistentTombstoneEntity() override;
 
   // Factory function for PersistentTombstoneEntity.
-  static std::unique_ptr<LoopbackServerEntity> Create(
+  static std::unique_ptr<LoopbackServerEntity> CreateFromEntity(
       const sync_pb::SyncEntity& id);
 
+  static std::unique_ptr<LoopbackServerEntity> CreateNew(
+      const std::string& id,
+      const std::string& client_defined_unique_tag);
+
   // LoopbackServerEntity implementation.
   bool RequiresParentId() const override;
   std::string GetParentId() const override;
@@ -34,7 +38,11 @@
  private:
   PersistentTombstoneEntity(const std::string& id,
                             int64_t version,
-                            const syncer::ModelType& model_type);
+                            const syncer::ModelType& model_type,
+                            const std::string& client_defined_unique_tag);
+
+  // The tag for this entity.
+  const std::string client_defined_unique_tag_;
 };
 
 }  // namespace syncer
diff --git a/components/sync/engine_impl/loopback_server/persistent_unique_client_entity.cc b/components/sync/engine_impl/loopback_server/persistent_unique_client_entity.cc
index 9a2b872..c2bdcf7 100644
--- a/components/sync/engine_impl/loopback_server/persistent_unique_client_entity.cc
+++ b/components/sync/engine_impl/loopback_server/persistent_unique_client_entity.cc
@@ -5,6 +5,9 @@
 #include "components/sync/engine_impl/loopback_server/persistent_unique_client_entity.h"
 
 #include "base/guid.h"
+#include "base/rand_util.h"
+#include "base/strings/string_number_conversions.h"
+#include "components/sync/base/hash_util.h"
 #include "components/sync/engine_impl/loopback_server/persistent_permanent_entity.h"
 #include "components/sync/protocol/sync.pb.h"
 
@@ -31,13 +34,21 @@
 PersistentUniqueClientEntity::~PersistentUniqueClientEntity() {}
 
 // static
-std::unique_ptr<LoopbackServerEntity> PersistentUniqueClientEntity::Create(
+std::unique_ptr<LoopbackServerEntity>
+PersistentUniqueClientEntity::CreateFromEntity(
     const sync_pb::SyncEntity& client_entity) {
-  CHECK(client_entity.has_client_defined_unique_tag())
-      << "A PersistentUniqueClientEntity must have a client-defined unique "
-         "tag.";
   ModelType model_type = GetModelTypeFromSpecifics(client_entity.specifics());
-  string id = EffectiveIdForClientTaggedEntity(client_entity);
+  CHECK_NE(client_entity.has_client_defined_unique_tag(),
+           syncer::CommitOnlyTypes().Has(model_type))
+      << "A UniqueClientEntity should have a client-defined unique tag iff it "
+         "is not a CommitOnly type.";
+  // Without model type specific logic for each CommitOnly type, we cannot infer
+  // a reasonable tag from the specifics. We need uniqueness for how the server
+  // holds onto all objects, so simply make a new tag from a random  number.
+  string effective_tag = client_entity.has_client_defined_unique_tag()
+                             ? client_entity.client_defined_unique_tag()
+                             : base::Uint64ToString(base::RandUint64());
+  string id = LoopbackServerEntity::CreateId(model_type, effective_tag);
   return std::unique_ptr<LoopbackServerEntity>(new PersistentUniqueClientEntity(
       id, model_type, client_entity.version(), client_entity.name(),
       client_entity.client_defined_unique_tag(), client_entity.specifics(),
@@ -45,11 +56,17 @@
 }
 
 // static
-std::string PersistentUniqueClientEntity::EffectiveIdForClientTaggedEntity(
-    const sync_pb::SyncEntity& entity) {
-  return LoopbackServerEntity::CreateId(
-      GetModelTypeFromSpecifics(entity.specifics()),
-      entity.client_defined_unique_tag());
+std::unique_ptr<LoopbackServerEntity>
+PersistentUniqueClientEntity::CreateFromEntitySpecifics(
+    const string& name,
+    const sync_pb::EntitySpecifics& entity_specifics) {
+  ModelType model_type = GetModelTypeFromSpecifics(entity_specifics);
+  string client_defined_unique_tag = GenerateSyncableHash(model_type, name);
+  string id =
+      LoopbackServerEntity::CreateId(model_type, client_defined_unique_tag);
+  return std::unique_ptr<LoopbackServerEntity>(new PersistentUniqueClientEntity(
+      id, model_type, 0, name, client_defined_unique_tag, entity_specifics,
+      1337, 1337));
 }
 
 bool PersistentUniqueClientEntity::RequiresParentId() const {
diff --git a/components/sync/engine_impl/loopback_server/persistent_unique_client_entity.h b/components/sync/engine_impl/loopback_server/persistent_unique_client_entity.h
index 8e03900..746f0bd 100644
--- a/components/sync/engine_impl/loopback_server/persistent_unique_client_entity.h
+++ b/components/sync/engine_impl/loopback_server/persistent_unique_client_entity.h
@@ -39,12 +39,14 @@
   ~PersistentUniqueClientEntity() override;
 
   // Factory function for creating a PersistentUniqueClientEntity.
-  static std::unique_ptr<LoopbackServerEntity> Create(
+  static std::unique_ptr<LoopbackServerEntity> CreateFromEntity(
       const sync_pb::SyncEntity& client_entity);
 
-  // Derives an ID from a unique client tagged entity.
-  static std::string EffectiveIdForClientTaggedEntity(
-      const sync_pb::SyncEntity& entity);
+  // Factory function for creating a PersistentUniqueClientEntity for use in the
+  // FakeServer injection API.
+  static std::unique_ptr<LoopbackServerEntity> CreateFromEntitySpecifics(
+      const std::string& name,
+      const sync_pb::EntitySpecifics& entity_specifics);
 
   // LoopbackServerEntity implementation.
   bool RequiresParentId() const override;
diff --git a/components/sync/test/fake_server/android/fake_server_helper_android.cc b/components/sync/test/fake_server/android/fake_server_helper_android.cc
index 6ce249c..e3f61f76 100644
--- a/components/sync/test/fake_server/android/fake_server_helper_android.cc
+++ b/components/sync/test/fake_server/android/fake_server_helper_android.cc
@@ -19,8 +19,6 @@
 #include "components/sync/test/fake_server/fake_server.h"
 #include "components/sync/test/fake_server/fake_server_network_resources.h"
 #include "components/sync/test/fake_server/fake_server_verifier.h"
-#include "components/sync/test/fake_server/tombstone_entity.h"
-#include "components/sync/test/fake_server/unique_client_entity.h"
 #include "jni/FakeServerHelper_jni.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "url/gurl.h"
@@ -146,7 +144,7 @@
                              &entity_specifics);
 
   fake_server_ptr->InjectEntity(
-      fake_server::UniqueClientEntity::CreateForInjection(
+      syncer::PersistentUniqueClientEntity::CreateFromEntitySpecifics(
           base::android::ConvertJavaStringToUTF8(env, name), entity_specifics));
 }
 
@@ -223,7 +221,7 @@
     const JavaParamRef<jstring>& parent_id) {
   fake_server::FakeServer* fake_server_ptr =
       reinterpret_cast<fake_server::FakeServer*>(fake_server);
-  std::unique_ptr<fake_server::FakeServerEntity> bookmark =
+  std::unique_ptr<syncer::LoopbackServerEntity> bookmark =
       CreateBookmarkEntity(env, title, url, parent_id);
   sync_pb::SyncEntity proto;
   bookmark->SerializeAsProto(&proto);
@@ -258,7 +256,7 @@
       proto.specifics());
 }
 
-std::unique_ptr<fake_server::FakeServerEntity>
+std::unique_ptr<syncer::LoopbackServerEntity>
 FakeServerHelperAndroid::CreateBookmarkEntity(JNIEnv* env,
                                               jstring title,
                                               jstring url,
@@ -284,10 +282,9 @@
     JNIEnv* env,
     const JavaParamRef<jobject>& obj,
     jlong fake_server) {
-  fake_server::FakeServer* fake_server_ptr =
-      reinterpret_cast<fake_server::FakeServer*>(fake_server);
-  return base::android::ConvertUTF8ToJavaString(
-      env, fake_server_ptr->GetBookmarkBarFolderId());
+  // Rather hard code this here then incur the cost of yet another method.
+  // It is very unlikely that this will ever change.
+  return base::android::ConvertUTF8ToJavaString(env, "32904_bookmark_bar");
 }
 
 void FakeServerHelperAndroid::DeleteEntity(JNIEnv* env,
@@ -298,7 +295,7 @@
       reinterpret_cast<fake_server::FakeServer*>(fake_server);
   std::string native_id = base::android::ConvertJavaStringToUTF8(env, id);
   fake_server_ptr->InjectEntity(
-      fake_server::TombstoneEntity::Create(native_id, std::string()));
+      syncer::PersistentTombstoneEntity::CreateNew(native_id, std::string()));
 }
 
 void FakeServerHelperAndroid::ClearServerData(JNIEnv* env,
diff --git a/components/sync/test/fake_server/android/fake_server_helper_android.h b/components/sync/test/fake_server/android/fake_server_helper_android.h
index 58cd7edf..f5d6323a 100644
--- a/components/sync/test/fake_server/android/fake_server_helper_android.h
+++ b/components/sync/test/fake_server/android/fake_server_helper_android.h
@@ -144,7 +144,7 @@
                                   sync_pb::EntitySpecifics* entity_specifics);
 
   // Creates a bookmark entity.
-  std::unique_ptr<fake_server::FakeServerEntity> CreateBookmarkEntity(
+  std::unique_ptr<syncer::LoopbackServerEntity> CreateBookmarkEntity(
       JNIEnv* env,
       jstring title,
       jstring url,
diff --git a/components/sync/test/fake_server/bookmark_entity.cc b/components/sync/test/fake_server/bookmark_entity.cc
deleted file mode 100644
index a301a31b..0000000
--- a/components/sync/test/fake_server/bookmark_entity.cc
+++ /dev/null
@@ -1,119 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/sync/test/fake_server/bookmark_entity.h"
-
-#include "base/guid.h"
-
-using std::string;
-
-namespace fake_server {
-namespace {
-
-// Returns true if and only if |client_entity| is a bookmark.
-bool IsBookmark(const sync_pb::SyncEntity& client_entity) {
-  return syncer::GetModelType(client_entity) == syncer::BOOKMARKS;
-}
-
-}  // namespace
-
-BookmarkEntity::~BookmarkEntity() {}
-
-// static
-std::unique_ptr<FakeServerEntity> BookmarkEntity::CreateNew(
-    const sync_pb::SyncEntity& client_entity,
-    const string& parent_id,
-    const string& client_guid) {
-  CHECK_EQ(0, client_entity.version()) << "New entities must have version = 0.";
-  CHECK(IsBookmark(client_entity)) << "The given entity must be a bookmark.";
-
-  const string id =
-      FakeServerEntity::CreateId(syncer::BOOKMARKS, base::GenerateGUID());
-  const string originator_cache_guid = client_guid;
-  const string originator_client_item_id = client_entity.id_string();
-
-  return std::unique_ptr<FakeServerEntity>(new BookmarkEntity(
-      id, client_entity.version(), client_entity.name(), originator_cache_guid,
-      originator_client_item_id, client_entity.unique_position(),
-      client_entity.specifics(), client_entity.folder(), parent_id,
-      client_entity.ctime(), client_entity.mtime()));
-}
-
-// static
-std::unique_ptr<FakeServerEntity> BookmarkEntity::CreateUpdatedVersion(
-    const sync_pb::SyncEntity& client_entity,
-    const FakeServerEntity& current_server_entity,
-    const string& parent_id) {
-  CHECK_NE(0, client_entity.version()) << "Existing entities must not have a "
-                                       << "version = 0.";
-  CHECK(IsBookmark(client_entity)) << "The given entity must be a bookmark.";
-
-  const BookmarkEntity& current_bookmark_entity =
-      static_cast<const BookmarkEntity&>(current_server_entity);
-  const string originator_cache_guid =
-      current_bookmark_entity.originator_cache_guid_;
-  const string originator_client_item_id =
-      current_bookmark_entity.originator_client_item_id_;
-
-  return std::unique_ptr<FakeServerEntity>(new BookmarkEntity(
-      client_entity.id_string(), client_entity.version(), client_entity.name(),
-      originator_cache_guid, originator_client_item_id,
-      client_entity.unique_position(), client_entity.specifics(),
-      client_entity.folder(), parent_id, client_entity.ctime(),
-      client_entity.mtime()));
-}
-
-BookmarkEntity::BookmarkEntity(const string& id,
-                               int64_t version,
-                               const string& name,
-                               const string& originator_cache_guid,
-                               const string& originator_client_item_id,
-                               const sync_pb::UniquePosition& unique_position,
-                               const sync_pb::EntitySpecifics& specifics,
-                               bool is_folder,
-                               const string& parent_id,
-                               int64_t creation_time,
-                               int64_t last_modified_time)
-    : FakeServerEntity(id, string(), syncer::BOOKMARKS, version, name),
-      originator_cache_guid_(originator_cache_guid),
-      originator_client_item_id_(originator_client_item_id),
-      unique_position_(unique_position),
-      is_folder_(is_folder),
-      parent_id_(parent_id),
-      creation_time_(creation_time),
-      last_modified_time_(last_modified_time) {
-  SetSpecifics(specifics);
-}
-
-void BookmarkEntity::SetParentId(const string& parent_id) {
-  parent_id_ = parent_id;
-}
-
-bool BookmarkEntity::RequiresParentId() const {
-  // Bookmarks are stored as a hierarchy. All bookmarks must have a parent ID.
-  return true;
-}
-
-string BookmarkEntity::GetParentId() const {
-  return parent_id_;
-}
-
-void BookmarkEntity::SerializeAsProto(sync_pb::SyncEntity* proto) const {
-  FakeServerEntity::SerializeBaseProtoFields(proto);
-
-  proto->set_originator_cache_guid(originator_cache_guid_);
-  proto->set_originator_client_item_id(originator_client_item_id_);
-
-  proto->set_ctime(creation_time_);
-  proto->set_mtime(last_modified_time_);
-
-  sync_pb::UniquePosition* unique_position = proto->mutable_unique_position();
-  unique_position->CopyFrom(unique_position_);
-}
-
-bool BookmarkEntity::IsFolder() const {
-  return is_folder_;
-}
-
-}  // namespace fake_server
diff --git a/components/sync/test/fake_server/bookmark_entity.h b/components/sync/test/fake_server/bookmark_entity.h
deleted file mode 100644
index 4eb99a3..0000000
--- a/components/sync/test/fake_server/bookmark_entity.h
+++ /dev/null
@@ -1,75 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef COMPONENTS_SYNC_TEST_FAKE_SERVER_BOOKMARK_ENTITY_H_
-#define COMPONENTS_SYNC_TEST_FAKE_SERVER_BOOKMARK_ENTITY_H_
-
-#include <stdint.h>
-
-#include <map>
-#include <memory>
-#include <string>
-
-#include "components/sync/base/model_type.h"
-#include "components/sync/protocol/sync.pb.h"
-#include "components/sync/test/fake_server/fake_server_entity.h"
-
-namespace fake_server {
-
-// A bookmark version of FakeServerEntity. This type represents entities that
-// are non-deleted, client-created, and not unique per client account.
-class BookmarkEntity : public FakeServerEntity {
- public:
-  ~BookmarkEntity() override;
-
-  // Factory function for BookmarkEntity. This factory should be used only for
-  // the first time that a specific bookmark is seen by the server.
-  static std::unique_ptr<FakeServerEntity> CreateNew(
-      const sync_pb::SyncEntity& client_entity,
-      const std::string& parent_id,
-      const std::string& client_guid);
-
-  // Factory function for BookmarkEntity. The server's current entity for this
-  // ID, |current_server_entity|, is passed here because the client does not
-  // always send the complete entity over the wire. This requires copying of
-  // some of the existing entity when creating a new entity.
-  static std::unique_ptr<FakeServerEntity> CreateUpdatedVersion(
-      const sync_pb::SyncEntity& client_entity,
-      const FakeServerEntity& current_server_entity,
-      const std::string& parent_id);
-
-  BookmarkEntity(const std::string& id,
-                 int64_t version,
-                 const std::string& name,
-                 const std::string& originator_cache_guid,
-                 const std::string& originator_client_item_id,
-                 const sync_pb::UniquePosition& unique_position,
-                 const sync_pb::EntitySpecifics& specifics,
-                 bool is_folder,
-                 const std::string& parent_id,
-                 int64_t creation_time,
-                 int64_t last_modified_time);
-
-  void SetParentId(const std::string& parent_id);
-
-  // FakeServerEntity implementation.
-  bool RequiresParentId() const override;
-  std::string GetParentId() const override;
-  void SerializeAsProto(sync_pb::SyncEntity* proto) const override;
-  bool IsFolder() const override;
-
- private:
-  // All member values have equivalent fields in SyncEntity.
-  std::string originator_cache_guid_;
-  std::string originator_client_item_id_;
-  sync_pb::UniquePosition unique_position_;
-  bool is_folder_;
-  std::string parent_id_;
-  int64_t creation_time_;
-  int64_t last_modified_time_;
-};
-
-}  // namespace fake_server
-
-#endif  // COMPONENTS_SYNC_TEST_FAKE_SERVER_BOOKMARK_ENTITY_H_
diff --git a/components/sync/test/fake_server/bookmark_entity_builder.cc b/components/sync/test/fake_server/bookmark_entity_builder.cc
index 599405fa..e9ca353 100644
--- a/components/sync/test/fake_server/bookmark_entity_builder.cc
+++ b/components/sync/test/fake_server/bookmark_entity_builder.cc
@@ -12,13 +12,14 @@
 #include "components/sync/base/hash_util.h"
 #include "components/sync/base/time.h"
 #include "components/sync/base/unique_position.h"
+#include "components/sync/engine_impl/loopback_server/persistent_bookmark_entity.h"
 #include "components/sync/protocol/sync.pb.h"
-#include "components/sync/test/fake_server/bookmark_entity.h"
 
 using std::string;
 using syncer::GenerateSyncableBookmarkHash;
+using syncer::LoopbackServerEntity;
 
-// A version must be passed when creating a FakeServerEntity, but this value
+// A version must be passed when creating a LoopbackServerEntity, but this value
 // is overrideen immediately when saving the entity in FakeServer.
 const int64_t kUnusedVersion = 0L;
 
@@ -44,10 +45,10 @@
   parent_id_ = parent_id;
 }
 
-std::unique_ptr<FakeServerEntity> BookmarkEntityBuilder::BuildBookmark(
+std::unique_ptr<LoopbackServerEntity> BookmarkEntityBuilder::BuildBookmark(
     const GURL& url) {
   if (!url.is_valid()) {
-    return base::WrapUnique<FakeServerEntity>(nullptr);
+    return base::WrapUnique<LoopbackServerEntity>(nullptr);
   }
 
   sync_pb::EntitySpecifics entity_specifics = CreateBaseEntitySpecifics();
@@ -56,7 +57,7 @@
   return Build(entity_specifics, kIsNotFolder);
 }
 
-std::unique_ptr<FakeServerEntity> BookmarkEntityBuilder::BuildFolder() {
+std::unique_ptr<LoopbackServerEntity> BookmarkEntityBuilder::BuildFolder() {
   const bool kIsFolder = true;
   return Build(CreateBaseEntitySpecifics(), kIsFolder);
 }
@@ -71,7 +72,7 @@
   return entity_specifics;
 }
 
-std::unique_ptr<FakeServerEntity> BookmarkEntityBuilder::Build(
+std::unique_ptr<LoopbackServerEntity> BookmarkEntityBuilder::Build(
     const sync_pb::EntitySpecifics& entity_specifics,
     bool is_folder) {
   sync_pb::UniquePosition unique_position;
@@ -81,16 +82,18 @@
   syncer::UniquePosition::FromInt64(0, suffix).ToProto(&unique_position);
 
   if (parent_id_.empty()) {
-    parent_id_ = FakeServerEntity::CreateId(syncer::BOOKMARKS, "bookmark_bar");
+    parent_id_ =
+        LoopbackServerEntity::CreateId(syncer::BOOKMARKS, "bookmark_bar");
   }
 
   const string id =
-      FakeServerEntity::CreateId(syncer::BOOKMARKS, base::GenerateGUID());
+      LoopbackServerEntity::CreateId(syncer::BOOKMARKS, base::GenerateGUID());
 
-  return base::WrapUnique<FakeServerEntity>(new BookmarkEntity(
-      id, kUnusedVersion, title_, originator_cache_guid_,
-      originator_client_item_id_, unique_position, entity_specifics, is_folder,
-      parent_id_, kDefaultTime, kDefaultTime));
+  return base::WrapUnique<LoopbackServerEntity>(
+      new syncer::PersistentBookmarkEntity(
+          id, kUnusedVersion, title_, originator_cache_guid_,
+          originator_client_item_id_, unique_position, entity_specifics,
+          is_folder, parent_id_, kDefaultTime, kDefaultTime));
 }
 
 }  // namespace fake_server
diff --git a/components/sync/test/fake_server/bookmark_entity_builder.h b/components/sync/test/fake_server/bookmark_entity_builder.h
index b1c1586..10cb784 100644
--- a/components/sync/test/fake_server/bookmark_entity_builder.h
+++ b/components/sync/test/fake_server/bookmark_entity_builder.h
@@ -9,7 +9,7 @@
 #include <string>
 
 #include "components/sync/base/model_type.h"
-#include "components/sync/test/fake_server/fake_server_entity.h"
+#include "components/sync/engine_impl/loopback_server/loopback_server_entity.h"
 #include "url/gurl.h"
 
 namespace fake_server {
@@ -29,22 +29,22 @@
   // the bookmark will be included in the bookmarks bar.
   void SetParentId(const std::string& parent_id);
 
-  // Builds and returns a FakeServerEntity representing a bookmark. Returns null
-  // if the entity could not be built.
-  std::unique_ptr<FakeServerEntity> BuildBookmark(const GURL& url);
+  // Builds and returns a LoopbackServerEntity representing a bookmark. Returns
+  // null if the entity could not be built.
+  std::unique_ptr<syncer::LoopbackServerEntity> BuildBookmark(const GURL& url);
 
-  // Builds and returns a FakeServerEntity representing a bookmark folder.
+  // Builds and returns a LoopbackServerEntity representing a bookmark folder.
   // Returns null if the entity could not be built.
-  std::unique_ptr<FakeServerEntity> BuildFolder();
+  std::unique_ptr<syncer::LoopbackServerEntity> BuildFolder();
 
  private:
   // Creates an EntitySpecifics and pre-populates its BookmarkSpecifics with
   // the entity's title.
   sync_pb::EntitySpecifics CreateBaseEntitySpecifics() const;
 
-  // Builds the parts of a FakeServerEntity common to both normal bookmarks and
-  // folders.
-  std::unique_ptr<FakeServerEntity> Build(
+  // Builds the parts of a LoopbackServerEntity common to both normal bookmarks
+  // and folders.
+  std::unique_ptr<syncer::LoopbackServerEntity> Build(
       const sync_pb::EntitySpecifics& entity_specifics,
       bool is_folder);
 
diff --git a/components/sync/test/fake_server/fake_server.cc b/components/sync/test/fake_server/fake_server.cc
index 9f490a1..f3aa2928 100644
--- a/components/sync/test/fake_server/fake_server.cc
+++ b/components/sync/test/fake_server/fake_server.cc
@@ -17,10 +17,8 @@
 #include "base/strings/string_util.h"
 #include "base/strings/stringprintf.h"
 #include "base/synchronization/lock.h"
-#include "components/sync/test/fake_server/bookmark_entity.h"
-#include "components/sync/test/fake_server/permanent_entity.h"
-#include "components/sync/test/fake_server/tombstone_entity.h"
-#include "components/sync/test/fake_server/unique_client_entity.h"
+#include "base/threading/thread_restrictions.h"
+#include "components/sync/engine_impl/net/server_connection_manager.h"
 #include "net/base/net_errors.h"
 #include "net/http/http_status_code.h"
 
@@ -28,195 +26,39 @@
 using std::vector;
 using syncer::GetModelType;
 using syncer::GetModelTypeFromSpecifics;
+using syncer::LoopbackServer;
+using syncer::LoopbackServerEntity;
 using syncer::ModelType;
 using syncer::ModelTypeSet;
 
 namespace fake_server {
 
-class FakeServerEntity;
-
-namespace {
-
-// The default keystore key.
-static const char kDefaultKeystoreKey[] = "1111111111111111";
-
-// Properties of the bookmark bar permanent folder.
-static const char kBookmarkBarFolderServerTag[] = "bookmark_bar";
-static const char kBookmarkBarFolderName[] = "Bookmark Bar";
-
-// Properties of the other bookmarks permanent folder.
-static const char kOtherBookmarksFolderServerTag[] = "other_bookmarks";
-static const char kOtherBookmarksFolderName[] = "Other Bookmarks";
-
-// Properties of the synced bookmarks permanent folder.
-static const char kSyncedBookmarksFolderServerTag[] = "synced_bookmarks";
-static const char kSyncedBookmarksFolderName[] = "Synced Bookmarks";
-
-// A filter used during GetUpdates calls to determine what information to
-// send back to the client; filtering out old entities and tracking versions to
-// use in response progress markers. Note that only the GetUpdatesMessage's
-// from_progress_marker is used to determine this; legacy fields are ignored.
-class UpdateSieve {
- public:
-  explicit UpdateSieve(const sync_pb::GetUpdatesMessage& message)
-      : UpdateSieve(MessageToVersionMap(message)) {}
-  ~UpdateSieve() {}
-
-  // Sets the progress markers in |get_updates_response| based on the highest
-  // version between request progress markers and response entities.
-  void SetProgressMarkers(
-      sync_pb::GetUpdatesResponse* get_updates_response) const {
-    for (const auto& kv : response_version_map_) {
-      sync_pb::DataTypeProgressMarker* new_marker =
-          get_updates_response->add_new_progress_marker();
-      new_marker->set_data_type_id(
-          GetSpecificsFieldNumberFromModelType(kv.first));
-      new_marker->set_token(base::Int64ToString(kv.second));
-    }
-  }
-
-  // Determines whether the server should send an |entity| to the client as
-  // part of a GetUpdatesResponse. Update internal tracking of max versions as a
-  // side effect which will later be used to set response progress markers.
-  bool ClientWantsItem(const FakeServerEntity& entity) {
-    int64_t version = entity.GetVersion();
-    ModelType type = entity.model_type();
-    response_version_map_[type] =
-        std::max(response_version_map_[type], version);
-    auto it = request_version_map_.find(type);
-    return it == request_version_map_.end() ? false : it->second < version;
-  }
-
- private:
-  using ModelTypeToVersionMap = std::map<ModelType, int64_t>;
-
-  static UpdateSieve::ModelTypeToVersionMap MessageToVersionMap(
-      const sync_pb::GetUpdatesMessage& get_updates_message) {
-    CHECK_GT(get_updates_message.from_progress_marker_size(), 0)
-        << "A GetUpdates request must have at least one progress marker.";
-    ModelTypeToVersionMap request_version_map;
-
-    for (int i = 0; i < get_updates_message.from_progress_marker_size(); i++) {
-      sync_pb::DataTypeProgressMarker marker =
-          get_updates_message.from_progress_marker(i);
-
-      int64_t version = 0;
-      // Let the version remain zero if there is no token or an empty token (the
-      // first request for this type).
-      if (marker.has_token() && !marker.token().empty()) {
-        bool parsed = base::StringToInt64(marker.token(), &version);
-        CHECK(parsed) << "Unable to parse progress marker token.";
-      }
-
-      ModelType model_type =
-          syncer::GetModelTypeFromSpecificsFieldNumber(marker.data_type_id());
-      DCHECK(request_version_map.find(model_type) == request_version_map.end());
-      request_version_map[model_type] = version;
-    }
-    return request_version_map;
-  }
-
-  explicit UpdateSieve(const ModelTypeToVersionMap request_version_map)
-      : request_version_map_(request_version_map),
-        response_version_map_(request_version_map) {}
-
-  // The largest versions the client has seen before this request, and is used
-  // to filter entities to send back to clients. The values in this map are not
-  // updated after being initially set. The presence of a type in this map is a
-  // proxy for the desire to receive results about this type.
-  const ModelTypeToVersionMap request_version_map_;
-
-  // The largest versions seen between client and server, ultimately used to
-  // send progress markers back to the client.
-  ModelTypeToVersionMap response_version_map_;
-};
-
-// Returns whether |entity| is deleted or permanent.
-bool IsDeletedOrPermanent(const FakeServerEntity& entity) {
-  return entity.IsDeleted() || entity.IsPermanent();
-}
-
-}  // namespace
-
 FakeServer::FakeServer()
-    : version_(0),
-      store_birthday_(0),
-      authenticated_(true),
+    : authenticated_(true),
       error_type_(sync_pb::SyncEnums::SUCCESS),
       alternate_triggered_errors_(false),
       request_counter_(0),
       network_enabled_(true),
       weak_ptr_factory_(this) {
-  Init();
+  base::ThreadRestrictions::SetIOAllowed(true);
+  loopback_server_storage_ = base::MakeUnique<base::ScopedTempDir>();
+  if (!loopback_server_storage_->CreateUniqueTempDir()) {
+    NOTREACHED() << "Creating temp dir failed.";
+  }
+  loopback_server_ = base::MakeUnique<syncer::LoopbackServer>(
+      loopback_server_storage_->GetPath().AppendASCII("profile.pb"));
+  loopback_server_->set_observer_for_tests(this);
 }
 
 FakeServer::~FakeServer() {}
 
-void FakeServer::Init() {
-  keystore_keys_.push_back(kDefaultKeystoreKey);
-
-  const bool create_result = CreateDefaultPermanentItems();
-  DCHECK(create_result) << "Permanent items were not created successfully.";
-}
-
-bool FakeServer::CreatePermanentBookmarkFolder(const std::string& server_tag,
-                                               const std::string& name) {
-  DCHECK(thread_checker_.CalledOnValidThread());
-  std::unique_ptr<FakeServerEntity> entity =
-      PermanentEntity::Create(syncer::BOOKMARKS, server_tag, name,
-                              ModelTypeToRootTag(syncer::BOOKMARKS));
-  if (!entity)
-    return false;
-
-  SaveEntity(std::move(entity));
-  return true;
-}
-
-bool FakeServer::CreateDefaultPermanentItems() {
-  // Permanent folders are always required for Bookmarks (hierarchical
-  // structure) and Nigori (data stored in permanent root folder).
-  ModelTypeSet permanent_folder_types =
-      ModelTypeSet(syncer::BOOKMARKS, syncer::NIGORI);
-
-  for (ModelTypeSet::Iterator it = permanent_folder_types.First(); it.Good();
-       it.Inc()) {
-    ModelType model_type = it.Get();
-
-    std::unique_ptr<FakeServerEntity> top_level_entity =
-        PermanentEntity::CreateTopLevel(model_type);
-    if (!top_level_entity) {
-      return false;
-    }
-    SaveEntity(std::move(top_level_entity));
-
-    if (model_type == syncer::BOOKMARKS) {
-      if (!CreatePermanentBookmarkFolder(kBookmarkBarFolderServerTag,
-                                         kBookmarkBarFolderName))
-        return false;
-      if (!CreatePermanentBookmarkFolder(kOtherBookmarksFolderServerTag,
-                                         kOtherBookmarksFolderName))
-        return false;
-    }
-  }
-
-  return true;
-}
-
-void FakeServer::UpdateEntityVersion(FakeServerEntity* entity) {
-  entity->SetVersion(++version_);
-}
-
-void FakeServer::SaveEntity(std::unique_ptr<FakeServerEntity> entity) {
-  UpdateEntityVersion(entity.get());
-  entities_[entity->id()] = std::move(entity);
-}
-
 void FakeServer::HandleCommand(const string& request,
                                const base::Closure& completion_closure,
                                int* error_code,
                                int* response_code,
                                std::string* response) {
   DCHECK(thread_checker_.CalledOnValidThread());
+
   if (!network_enabled_) {
     *error_code = net::ERR_FAILED;
     *response_code = net::ERR_FAILED;
@@ -234,66 +76,43 @@
     return;
   }
 
-  sync_pb::ClientToServerMessage message;
-  bool parsed = message.ParseFromString(request);
-  CHECK(parsed) << "Unable to parse the ClientToServerMessage.";
-
   sync_pb::ClientToServerResponse response_proto;
-
-  if (message.has_store_birthday() &&
-      message.store_birthday() != GetStoreBirthday()) {
-    response_proto.set_error_code(sync_pb::SyncEnums::NOT_MY_BIRTHDAY);
-  } else if (error_type_ != sync_pb::SyncEnums::SUCCESS &&
-             ShouldSendTriggeredError()) {
+  *response_code = 200;
+  *error_code = 0;
+  if (error_type_ != sync_pb::SyncEnums::SUCCESS &&
+      ShouldSendTriggeredError()) {
     response_proto.set_error_code(error_type_);
   } else if (triggered_actionable_error_.get() && ShouldSendTriggeredError()) {
     sync_pb::ClientToServerResponse_Error* error =
         response_proto.mutable_error();
     error->CopyFrom(*(triggered_actionable_error_.get()));
   } else {
-    bool success = false;
+    sync_pb::ClientToServerMessage message;
+    bool parsed = message.ParseFromString(request);
+    CHECK(parsed) << "Unable to parse the ClientToServerMessage.";
     switch (message.message_contents()) {
       case sync_pb::ClientToServerMessage::GET_UPDATES:
         last_getupdates_message_ = message;
-        success = HandleGetUpdatesRequest(message.get_updates(),
-                                          response_proto.mutable_get_updates());
         break;
       case sync_pb::ClientToServerMessage::COMMIT:
         last_commit_message_ = message;
-        success = HandleCommitRequest(message.commit(),
-                                      message.invalidator_client_id(),
-                                      response_proto.mutable_commit());
-        break;
-      case sync_pb::ClientToServerMessage::CLEAR_SERVER_DATA:
-        ClearServerData();
-        response_proto.mutable_clear_server_data();
-        success = true;
         break;
       default:
-        *error_code = net::ERR_NOT_IMPLEMENTED;
-        *response_code = 0;
-        *response = string();
-        completion_closure.Run();
-        return;
+        break;
+        // Don't care.
     }
 
-    if (!success) {
-      // TODO(pvalenzuela): Add logging here so that tests have more info about
-      // the failure.
-      *error_code = net::ERR_FAILED;
-      *response_code = 0;
-      *response = string();
-      completion_closure.Run();
-      return;
-    }
-
-    response_proto.set_error_code(sync_pb::SyncEnums::SUCCESS);
+    int64_t response_code_large;
+    syncer::HttpResponse::ServerConnectionCode server_status;
+    base::ThreadRestrictions::SetIOAllowed(true);
+    loopback_server_->HandleCommand(request, &server_status,
+                                    &response_code_large, response);
+    *response_code = static_cast<int>(response_code_large);
+    completion_closure.Run();
+    return;
   }
 
-  response_proto.set_store_birthday(GetStoreBirthday());
-
-  *error_code = 0;
-  *response_code = net::HTTP_OK;
+  response_proto.set_store_birthday(loopback_server_->GetStoreBirthday());
   *response = response_proto.SerializeAsString();
   completion_closure.Run();
 }
@@ -315,289 +134,45 @@
   return true;
 }
 
-bool FakeServer::HandleGetUpdatesRequest(
-    const sync_pb::GetUpdatesMessage& get_updates,
-    sync_pb::GetUpdatesResponse* response) {
-  // TODO(pvalenzuela): Implement batching instead of sending all information
-  // at once.
-  response->set_changes_remaining(0);
-
-  auto sieve = base::MakeUnique<UpdateSieve>(get_updates);
-
-  // This folder is called "Synced Bookmarks" by sync and is renamed
-  // "Mobile Bookmarks" by the mobile client UIs.
-  if (get_updates.create_mobile_bookmarks_folder() &&
-      !CreatePermanentBookmarkFolder(kSyncedBookmarksFolderServerTag,
-                                     kSyncedBookmarksFolderName)) {
-    return false;
-  }
-
-  bool send_encryption_keys_based_on_nigori = false;
-  for (EntityMap::const_iterator it = entities_.begin(); it != entities_.end();
-       ++it) {
-    const FakeServerEntity& entity = *it->second;
-    if (sieve->ClientWantsItem(entity)) {
-      sync_pb::SyncEntity* response_entity = response->add_entries();
-      entity.SerializeAsProto(response_entity);
-
-      if (entity.model_type() == syncer::NIGORI) {
-        send_encryption_keys_based_on_nigori =
-            response_entity->specifics().nigori().passphrase_type() ==
-            sync_pb::NigoriSpecifics::KEYSTORE_PASSPHRASE;
-      }
-    }
-  }
-
-  if (send_encryption_keys_based_on_nigori ||
-      get_updates.need_encryption_key()) {
-    for (vector<string>::iterator it = keystore_keys_.begin();
-         it != keystore_keys_.end(); ++it) {
-      response->add_encryption_keys(*it);
-    }
-  }
-
-  sieve->SetProgressMarkers(response);
-  return true;
-}
-
-string FakeServer::CommitEntity(
-    const sync_pb::SyncEntity& client_entity,
-    sync_pb::CommitResponse_EntryResponse* entry_response,
-    const string& client_guid,
-    const string& parent_id) {
-  if (client_entity.version() == 0 && client_entity.deleted()) {
-    return string();
-  }
-
-  std::unique_ptr<FakeServerEntity> entity;
-  syncer::ModelType type = GetModelType(client_entity);
-  if (client_entity.deleted()) {
-    entity = TombstoneEntity::Create(client_entity.id_string(),
-                                     client_entity.client_defined_unique_tag());
-    DeleteChildren(client_entity.id_string());
-  } else if (type == syncer::NIGORI) {
-    // NIGORI is the only permanent item type that should be updated by the
-    // client.
-    EntityMap::const_iterator iter = entities_.find(client_entity.id_string());
-    CHECK(iter != entities_.end());
-    entity = PermanentEntity::CreateUpdatedNigoriEntity(client_entity,
-                                                        *iter->second);
-  } else if (type == syncer::BOOKMARKS) {
-    // TODO(pvalenzuela): Validate entity's parent ID.
-    EntityMap::const_iterator iter = entities_.find(client_entity.id_string());
-    if (iter != entities_.end()) {
-      entity = BookmarkEntity::CreateUpdatedVersion(client_entity,
-                                                    *iter->second, parent_id);
-    } else {
-      entity = BookmarkEntity::CreateNew(client_entity, parent_id, client_guid);
-    }
-  } else {
-    entity = UniqueClientEntity::Create(client_entity);
-  }
-
-  const std::string id = entity->id();
-  SaveEntity(std::move(entity));
-  BuildEntryResponseForSuccessfulCommit(id, entry_response);
-  return id;
-}
-
 void FakeServer::OverrideResponseType(
-    ResponseTypeProvider response_type_override) {
-  response_type_override_ = std::move(response_type_override);
-}
-
-void FakeServer::BuildEntryResponseForSuccessfulCommit(
-    const std::string& entity_id,
-    sync_pb::CommitResponse_EntryResponse* entry_response) {
-  EntityMap::const_iterator iter = entities_.find(entity_id);
-  CHECK(iter != entities_.end());
-  const FakeServerEntity& entity = *iter->second;
-  entry_response->set_response_type(response_type_override_
-                                        ? response_type_override_.Run(entity)
-                                        : sync_pb::CommitResponse::SUCCESS);
-  entry_response->set_id_string(entity.id());
-
-  if (entity.IsDeleted()) {
-    entry_response->set_version(entity.GetVersion() + 1);
-  } else {
-    entry_response->set_version(entity.GetVersion());
-    entry_response->set_name(entity.GetName());
-  }
-}
-
-bool FakeServer::IsChild(const string& id, const string& potential_parent_id) {
-  EntityMap::const_iterator iter = entities_.find(id);
-  if (iter == entities_.end()) {
-    // We've hit an ID (probably the imaginary root entity) that isn't stored
-    // by the server, so it can't be a child.
-    return false;
-  }
-
-  const FakeServerEntity& entity = *iter->second;
-  if (entity.GetParentId() == potential_parent_id)
-    return true;
-
-  // Recursively look up the tree.
-  return IsChild(entity.GetParentId(), potential_parent_id);
-}
-
-void FakeServer::DeleteChildren(const string& id) {
-  std::vector<std::unique_ptr<FakeServerEntity>> tombstones;
-  // Find all the children of id.
-  for (const auto& entity : entities_) {
-    if (IsChild(entity.first, id)) {
-      tombstones.push_back(TombstoneEntity::Create(
-          entity.first, entity.second->client_defined_unique_tag()));
-    }
-  }
-
-  for (auto& tombstone : tombstones) {
-    SaveEntity(std::move(tombstone));
-  }
-}
-
-bool FakeServer::HandleCommitRequest(const sync_pb::CommitMessage& commit,
-                                     const std::string& invalidator_client_id,
-                                     sync_pb::CommitResponse* response) {
-  std::map<string, string> client_to_server_ids;
-  string guid = commit.cache_guid();
-  ModelTypeSet committed_model_types;
-
-  // TODO(pvalenzuela): Add validation of CommitMessage.entries.
-  ::google::protobuf::RepeatedPtrField<sync_pb::SyncEntity>::const_iterator it;
-  for (it = commit.entries().begin(); it != commit.entries().end(); ++it) {
-    sync_pb::CommitResponse_EntryResponse* entry_response =
-        response->add_entryresponse();
-
-    sync_pb::SyncEntity client_entity = *it;
-    string parent_id = client_entity.parent_id_string();
-    if (client_to_server_ids.find(parent_id) != client_to_server_ids.end()) {
-      parent_id = client_to_server_ids[parent_id];
-    }
-
-    const string entity_id =
-        CommitEntity(client_entity, entry_response, guid, parent_id);
-    if (entity_id.empty()) {
-      return false;
-    }
-
-    // Record the ID if it was renamed.
-    if (entity_id != client_entity.id_string()) {
-      client_to_server_ids[client_entity.id_string()] = entity_id;
-    }
-
-    EntityMap::const_iterator iter = entities_.find(entity_id);
-    CHECK(iter != entities_.end());
-    committed_model_types.Put(iter->second->model_type());
-  }
-
-  for (auto& observer : observers_)
-    observer.OnCommit(invalidator_client_id, committed_model_types);
-
-  return true;
+    LoopbackServer::ResponseTypeProvider response_type_override) {
+  loopback_server_->OverrideResponseType(std::move(response_type_override));
 }
 
 std::unique_ptr<base::DictionaryValue>
 FakeServer::GetEntitiesAsDictionaryValue() {
   DCHECK(thread_checker_.CalledOnValidThread());
-  std::unique_ptr<base::DictionaryValue> dictionary(
-      new base::DictionaryValue());
-
-  // Initialize an empty ListValue for all ModelTypes.
-  ModelTypeSet all_types = ModelTypeSet::All();
-  for (ModelTypeSet::Iterator it = all_types.First(); it.Good(); it.Inc()) {
-    dictionary->Set(ModelTypeToString(it.Get()),
-                    base::MakeUnique<base::ListValue>());
-  }
-
-  for (EntityMap::const_iterator it = entities_.begin(); it != entities_.end();
-       ++it) {
-    const FakeServerEntity& entity = *it->second;
-    if (IsDeletedOrPermanent(entity)) {
-      // Tombstones are ignored as they don't represent current data. Folders
-      // are also ignored as current verification infrastructure does not
-      // consider them.
-      continue;
-    }
-    base::ListValue* list_value;
-    if (!dictionary->GetList(ModelTypeToString(entity.model_type()),
-                             &list_value)) {
-      return std::unique_ptr<base::DictionaryValue>();
-    }
-    // TODO(pvalenzuela): Store more data for each entity so additional
-    // verification can be performed. One example of additional verification
-    // is checking the correctness of the bookmark hierarchy.
-    list_value->AppendString(entity.GetName());
-  }
-
-  return dictionary;
+  return loopback_server_->GetEntitiesAsDictionaryValue();
 }
 
 std::vector<sync_pb::SyncEntity> FakeServer::GetSyncEntitiesByModelType(
     ModelType model_type) {
-  std::vector<sync_pb::SyncEntity> sync_entities;
   DCHECK(thread_checker_.CalledOnValidThread());
-  for (EntityMap::const_iterator it = entities_.begin(); it != entities_.end();
-       ++it) {
-    const FakeServerEntity& entity = *it->second;
-    if (!IsDeletedOrPermanent(entity) && entity.model_type() == model_type) {
-      sync_pb::SyncEntity sync_entity;
-      entity.SerializeAsProto(&sync_entity);
-      sync_entities.push_back(sync_entity);
-    }
-  }
-  return sync_entities;
+  return loopback_server_->GetSyncEntitiesByModelType(model_type);
 }
 
-void FakeServer::InjectEntity(std::unique_ptr<FakeServerEntity> entity) {
+void FakeServer::InjectEntity(std::unique_ptr<LoopbackServerEntity> entity) {
   DCHECK(thread_checker_.CalledOnValidThread());
-  SaveEntity(std::move(entity));
+  loopback_server_->SaveEntity(std::move(entity));
 }
 
 bool FakeServer::ModifyEntitySpecifics(
     const std::string& id,
     const sync_pb::EntitySpecifics& updated_specifics) {
-  EntityMap::const_iterator iter = entities_.find(id);
-  if (iter == entities_.end() ||
-      iter->second->model_type() !=
-          GetModelTypeFromSpecifics(updated_specifics)) {
-    return false;
-  }
-
-  FakeServerEntity* entity = iter->second.get();
-  entity->SetSpecifics(updated_specifics);
-  UpdateEntityVersion(entity);
-  return true;
+  return loopback_server_->ModifyEntitySpecifics(id, updated_specifics);
 }
 
 bool FakeServer::ModifyBookmarkEntity(
     const std::string& id,
     const std::string& parent_id,
     const sync_pb::EntitySpecifics& updated_specifics) {
-  EntityMap::const_iterator iter = entities_.find(id);
-  if (iter == entities_.end() ||
-      iter->second->model_type() != syncer::BOOKMARKS ||
-      GetModelTypeFromSpecifics(updated_specifics) != syncer::BOOKMARKS) {
-    return false;
-  }
-
-  BookmarkEntity* entity = static_cast<BookmarkEntity*>(iter->second.get());
-
-  entity->SetParentId(parent_id);
-  entity->SetSpecifics(updated_specifics);
-  if (updated_specifics.has_bookmark()) {
-    entity->SetName(updated_specifics.bookmark().title());
-  }
-  UpdateEntityVersion(entity);
-  return true;
+  return loopback_server_->ModifyBookmarkEntity(id, parent_id,
+                                                updated_specifics);
 }
 
 void FakeServer::ClearServerData() {
   DCHECK(thread_checker_.CalledOnValidThread());
-  entities_.clear();
-  keystore_keys_.clear();
-  ++store_birthday_;
-  Init();
+  loopback_server_->ClearServerData();
 }
 
 void FakeServer::SetAuthenticated() {
@@ -675,6 +250,12 @@
   observers_.RemoveObserver(observer);
 }
 
+void FakeServer::OnCommit(const std::string& committer_id,
+                          syncer::ModelTypeSet committed_model_types) {
+  for (auto& observer : observers_)
+    observer.OnCommit(committer_id, committed_model_types);
+}
+
 void FakeServer::EnableNetwork() {
   DCHECK(thread_checker_.CalledOnValidThread());
   network_enabled_ = true;
@@ -685,28 +266,9 @@
   network_enabled_ = false;
 }
 
-std::string FakeServer::GetBookmarkBarFolderId() const {
-  DCHECK(thread_checker_.CalledOnValidThread());
-  for (EntityMap::const_iterator it = entities_.begin(); it != entities_.end();
-       ++it) {
-    FakeServerEntity* entity = it->second.get();
-    if (entity->GetName() == kBookmarkBarFolderName && entity->IsFolder() &&
-        entity->model_type() == syncer::BOOKMARKS) {
-      return entity->id();
-    }
-  }
-  NOTREACHED() << "Bookmark Bar entity not found.";
-  return "";
-}
-
 base::WeakPtr<FakeServer> FakeServer::AsWeakPtr() {
   DCHECK(thread_checker_.CalledOnValidThread());
   return weak_ptr_factory_.GetWeakPtr();
 }
 
-std::string FakeServer::GetStoreBirthday() const {
-  DCHECK(thread_checker_.CalledOnValidThread());
-  return base::Int64ToString(store_birthday_);
-}
-
 }  // namespace fake_server
diff --git a/components/sync/test/fake_server/fake_server.h b/components/sync/test/fake_server/fake_server.h
index 9cd8ed5..1355e38a 100644
--- a/components/sync/test/fake_server/fake_server.h
+++ b/components/sync/test/fake_server/fake_server.h
@@ -13,18 +13,23 @@
 #include <vector>
 
 #include "base/callback.h"
+#include "base/files/scoped_temp_dir.h"
 #include "base/observer_list.h"
 #include "base/threading/thread_checker.h"
 #include "base/values.h"
 #include "components/sync/base/model_type.h"
+#include "components/sync/engine_impl/loopback_server/loopback_server.h"
+#include "components/sync/engine_impl/loopback_server/loopback_server_entity.h"
+#include "components/sync/engine_impl/loopback_server/persistent_bookmark_entity.h"
+#include "components/sync/engine_impl/loopback_server/persistent_tombstone_entity.h"
+#include "components/sync/engine_impl/loopback_server/persistent_unique_client_entity.h"
 #include "components/sync/protocol/sync.pb.h"
-#include "components/sync/test/fake_server/fake_server_entity.h"
 
 namespace fake_server {
 
 // A fake version of the Sync server used for testing. This class is not thread
 // safe.
-class FakeServer {
+class FakeServer : public syncer::LoopbackServer::ObserverForTests {
  public:
   class Observer {
    public:
@@ -37,7 +42,7 @@
   };
 
   FakeServer();
-  virtual ~FakeServer();
+  ~FakeServer() override;
 
   // Handles a /command POST (with the given |request|) to the server. Three
   // output arguments, |error_code|, |response_code|, and |response|, are used
@@ -64,7 +69,7 @@
 
   // Returns all entities stored by the server of the given |model_type|.
   // This method returns SyncEntity protocol buffer objects (instead of
-  // FakeServerEntity) so that callers can inspect datatype-specific data
+  // LoopbackServerEntity) so that callers can inspect datatype-specific data
   // (e.g., the URL of a session tab).
   std::vector<sync_pb::SyncEntity> GetSyncEntitiesByModelType(
       syncer::ModelType model_type);
@@ -72,7 +77,7 @@
   // Adds |entity| to the server's collection of entities. This method makes no
   // guarantees that the added entity will result in successful server
   // operations.
-  void InjectEntity(std::unique_ptr<FakeServerEntity> entity);
+  void InjectEntity(std::unique_ptr<syncer::LoopbackServerEntity> entity);
 
   // Modifies the entity on the server with the given |id|. The entity's
   // EntitySpecifics are replaced with |updated_specifics| and its version is
@@ -140,100 +145,30 @@
   // This can be used to trigger exponential backoff in the client.
   void DisableNetwork();
 
-  // Returns the entity ID of the Bookmark Bar folder.
-  std::string GetBookmarkBarFolderId() const;
+  // Implement LoopbackServer::ObserverForTests:
+  void OnCommit(const std::string& committer_id,
+                syncer::ModelTypeSet committed_model_types) override;
 
   // Returns the current FakeServer as a WeakPtr.
   base::WeakPtr<FakeServer> AsWeakPtr();
 
-  using ResponseTypeProvider =
-      base::RepeatingCallback<sync_pb::CommitResponse::ResponseType(
-          const FakeServerEntity& entity)>;
-
   // Use this callback to generate response types for entities. They will still
   // be "committed" and stored as normal, this only affects the response type
   // the client sees. This allows tests to still inspect what the client has
   // done, although not as useful of a mechanism for multi client tests. Care
   // should be taken when failing responses, as the client will go into
   // exponential backoff, which can cause tests to be slow or time out.
-  void OverrideResponseType(ResponseTypeProvider response_type_override);
+  void OverrideResponseType(
+      syncer::LoopbackServer::ResponseTypeProvider response_type_override);
 
  private:
-  using EntityMap = std::map<std::string, std::unique_ptr<FakeServerEntity>>;
-
-  // Gets FakeServer ready for syncing.
-  void Init();
-
-  // Processes a GetUpdates call.
-  bool HandleGetUpdatesRequest(const sync_pb::GetUpdatesMessage& get_updates,
-                               sync_pb::GetUpdatesResponse* response);
-
-  // Processes a Commit call.
-  bool HandleCommitRequest(const sync_pb::CommitMessage& message,
-                           const std::string& invalidator_client_id,
-                           sync_pb::CommitResponse* response);
-
-  // Creates and saves a permanent folder for Bookmarks (e.g., Bookmark Bar).
-  bool CreatePermanentBookmarkFolder(const std::string& server_tag,
-                                     const std::string& name);
-
-  // Inserts the default permanent items in |entities_|.
-  bool CreateDefaultPermanentItems();
-
-  // Saves a |entity| to |entities_|.
-  void SaveEntity(std::unique_ptr<FakeServerEntity> entity);
-
-  // Commits a client-side SyncEntity to the server as a FakeServerEntity.
-  // The client that sent the commit is identified via |client_guid|. The
-  // parent ID string present in |client_entity| should be ignored in favor
-  // of |parent_id|. If the commit is successful, the entity's server ID string
-  // is returned and a new FakeServerEntity is saved in |entities_|.
-  std::string CommitEntity(
-      const sync_pb::SyncEntity& client_entity,
-      sync_pb::CommitResponse_EntryResponse* entry_response,
-      const std::string& client_guid,
-      const std::string& parent_id);
-
-  // Populates |entry_response| based on the stored entity identified by
-  // |entity_id|. It is assumed that the entity identified by |entity_id| has
-  // already been stored using SaveEntity.
-  void BuildEntryResponseForSuccessfulCommit(
-      const std::string& entity_id,
-      sync_pb::CommitResponse_EntryResponse* entry_response);
-
-  // Determines whether the SyncEntity with id_string |id| is a child of an
-  // entity with id_string |potential_parent_id|.
-  bool IsChild(const std::string& id, const std::string& potential_parent_id);
-
-  // Creates and saves tombstones for all children of the entity with the given
-  // |id|. A tombstone is not created for the entity itself.
-  void DeleteChildren(const std::string& id);
-
   // Returns whether a triggered error should be sent for the request.
   bool ShouldSendTriggeredError() const;
 
-  // Updates the |entity| to a new version and increments the version counter
-  // that the server uses to assign versions.
-  void UpdateEntityVersion(FakeServerEntity* entity);
-
-  // Returns the store birthday.
-  std::string GetStoreBirthday() const;
-
-  // This is the last version number assigned to an entity. The next entity will
-  // have a version number of version_ + 1.
-  int64_t version_;
-
-  // The current store birthday value.
-  int64_t store_birthday_;
-
   // Whether the server should act as if incoming connections are properly
   // authenticated.
   bool authenticated_;
 
-  // All SyncEntity objects saved by the server. The key value is the entity's
-  // id string.
-  EntityMap entities_;
-
   // All Keystore keys known to the server.
   std::vector<std::string> keystore_keys_;
 
@@ -267,11 +202,12 @@
   sync_pb::ClientToServerMessage last_commit_message_;
   sync_pb::ClientToServerMessage last_getupdates_message_;
 
-  ResponseTypeProvider response_type_override_;
-
   // Used to verify that FakeServer is only used from one thread.
   base::ThreadChecker thread_checker_;
 
+  std::unique_ptr<syncer::LoopbackServer> loopback_server_;
+  std::unique_ptr<base::ScopedTempDir> loopback_server_storage_;
+
   // Creates WeakPtr versions of the current FakeServer. This must be the last
   // data member!
   base::WeakPtrFactory<FakeServer> weak_ptr_factory_;
diff --git a/components/sync/test/fake_server/fake_server_entity.h b/components/sync/test/fake_server/fake_server_entity.h
deleted file mode 100644
index 1e025797..0000000
--- a/components/sync/test/fake_server/fake_server_entity.h
+++ /dev/null
@@ -1,91 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef COMPONENTS_SYNC_TEST_FAKE_SERVER_FAKE_SERVER_ENTITY_H_
-#define COMPONENTS_SYNC_TEST_FAKE_SERVER_FAKE_SERVER_ENTITY_H_
-
-#include <stdint.h>
-
-#include <map>
-#include <string>
-
-#include "components/sync/base/model_type.h"
-#include "components/sync/protocol/sync.pb.h"
-
-namespace fake_server {
-
-// The representation of a Sync entity for the fake server.
-class FakeServerEntity {
- public:
-  // Creates an ID of the form <type><separator><inner-id> where
-  // <type> is the EntitySpecifics field number for |model_type|, <separator>
-  // is kIdSeparator, and <inner-id> is |inner_id|.
-  //
-  // If |inner_id| is globally unique, then the returned ID will also be
-  // globally unique.
-  static std::string CreateId(const syncer::ModelType& model_type,
-                              const std::string& inner_id);
-
-  // Returns the ID string of the top level node for the specified type.
-  static std::string GetTopLevelId(const syncer::ModelType& model_type);
-
-  virtual ~FakeServerEntity();
-  const std::string& id() const { return id_; }
-  const std::string& client_defined_unique_tag() const {
-    return client_defined_unique_tag_;
-  }
-  syncer::ModelType model_type() const { return model_type_; }
-  int64_t GetVersion() const;
-  void SetVersion(int64_t version);
-  const std::string& GetName() const;
-  void SetName(const std::string& name);
-
-  // Replaces |specifics_| with |updated_specifics|. This method is meant to be
-  // used to mimic a client commit.
-  void SetSpecifics(const sync_pb::EntitySpecifics& updated_specifics);
-
-  // Common data items needed by server
-  virtual bool RequiresParentId() const = 0;
-  virtual std::string GetParentId() const = 0;
-  virtual void SerializeAsProto(sync_pb::SyncEntity* proto) const = 0;
-  virtual bool IsDeleted() const;
-  virtual bool IsFolder() const;
-  virtual bool IsPermanent() const;
-
- protected:
-  // Extracts the ModelType from |id|. If |id| is malformed or does not contain
-  // a valid ModelType, UNSPECIFIED is returned.
-  static syncer::ModelType GetModelTypeFromId(const std::string& id);
-
-  FakeServerEntity(const std::string& id,
-                   const std::string& client_defined_unique_tag,
-                   const syncer::ModelType& model_type,
-                   int64_t version,
-                   const std::string& name);
-
-  void SerializeBaseProtoFields(sync_pb::SyncEntity* sync_entity) const;
-
- private:
-  // The entity's ID.
-  const std::string id_;
-
-  // The tag for this entity. Can be empty for bookmarks or permanent entities.
-  const std::string client_defined_unique_tag_;
-
-  // The ModelType that categorizes this entity.
-  const syncer::ModelType model_type_;
-
-  // The version of this entity.
-  int64_t version_;
-
-  // The name of the entity.
-  std::string name_;
-
-  // The EntitySpecifics for the entity.
-  sync_pb::EntitySpecifics specifics_;
-};
-
-}  // namespace fake_server
-
-#endif  // COMPONENTS_SYNC_TEST_FAKE_SERVER_FAKE_SERVER_ENTITY_H_
diff --git a/components/sync/test/fake_server/permanent_entity.cc b/components/sync/test/fake_server/permanent_entity.cc
deleted file mode 100644
index 8183fa6..0000000
--- a/components/sync/test/fake_server/permanent_entity.cc
+++ /dev/null
@@ -1,105 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/sync/test/fake_server/permanent_entity.h"
-
-#include "base/logging.h"
-#include "base/memory/ptr_util.h"
-
-using std::string;
-using syncer::ModelType;
-
-// The parent tag for children of the root entity. Entities with this parent are
-// referred to as top level enities.
-static const char kRootParentTag[] = "0";
-
-namespace fake_server {
-
-PermanentEntity::~PermanentEntity() {}
-
-// static
-std::unique_ptr<FakeServerEntity> PermanentEntity::Create(
-    const ModelType& model_type,
-    const string& server_tag,
-    const string& name,
-    const string& parent_server_tag) {
-  CHECK(model_type != syncer::UNSPECIFIED) << "The entity's ModelType is "
-                                           << "invalid.";
-  CHECK(!server_tag.empty()) << "A PermanentEntity must have a server tag.";
-  CHECK(!name.empty()) << "The entity must have a non-empty name.";
-  CHECK(!parent_server_tag.empty()) << "A PermanentEntity must have a parent "
-                                    << "server tag.";
-  CHECK(parent_server_tag != kRootParentTag) << "Top-level entities should not "
-                                             << "be created with this factory.";
-
-  string id = FakeServerEntity::CreateId(model_type, server_tag);
-  string parent_id = FakeServerEntity::CreateId(model_type, parent_server_tag);
-  sync_pb::EntitySpecifics entity_specifics;
-  AddDefaultFieldValue(model_type, &entity_specifics);
-  return std::unique_ptr<FakeServerEntity>(new PermanentEntity(
-      id, model_type, name, parent_id, server_tag, entity_specifics));
-}
-
-// static
-std::unique_ptr<FakeServerEntity> PermanentEntity::CreateTopLevel(
-    const ModelType& model_type) {
-  CHECK(model_type != syncer::UNSPECIFIED) << "The entity's ModelType is "
-                                           << "invalid.";
-  string server_tag = syncer::ModelTypeToRootTag(model_type);
-  string name = syncer::ModelTypeToString(model_type);
-  string id = FakeServerEntity::GetTopLevelId(model_type);
-  sync_pb::EntitySpecifics entity_specifics;
-  AddDefaultFieldValue(model_type, &entity_specifics);
-  return std::unique_ptr<FakeServerEntity>(new PermanentEntity(
-      id, model_type, name, kRootParentTag, server_tag, entity_specifics));
-}
-
-// static
-std::unique_ptr<FakeServerEntity> PermanentEntity::CreateUpdatedNigoriEntity(
-    const sync_pb::SyncEntity& client_entity,
-    const FakeServerEntity& current_server_entity) {
-  ModelType model_type = current_server_entity.model_type();
-  CHECK(model_type == syncer::NIGORI) << "This factory only supports NIGORI "
-                                      << "entities.";
-
-  return base::WrapUnique<FakeServerEntity>(new PermanentEntity(
-      current_server_entity.id(), model_type, current_server_entity.GetName(),
-      current_server_entity.GetParentId(),
-      syncer::ModelTypeToRootTag(model_type), client_entity.specifics()));
-}
-
-PermanentEntity::PermanentEntity(const string& id,
-                                 const ModelType& model_type,
-                                 const string& name,
-                                 const string& parent_id,
-                                 const string& server_defined_unique_tag,
-                                 const sync_pb::EntitySpecifics& specifics)
-    : FakeServerEntity(id, string(), model_type, 0, name),
-      server_defined_unique_tag_(server_defined_unique_tag),
-      parent_id_(parent_id) {
-  SetSpecifics(specifics);
-}
-
-bool PermanentEntity::RequiresParentId() const {
-  return true;
-}
-
-string PermanentEntity::GetParentId() const {
-  return parent_id_;
-}
-
-void PermanentEntity::SerializeAsProto(sync_pb::SyncEntity* proto) const {
-  FakeServerEntity::SerializeBaseProtoFields(proto);
-  proto->set_server_defined_unique_tag(server_defined_unique_tag_);
-}
-
-bool PermanentEntity::IsFolder() const {
-  return true;
-}
-
-bool PermanentEntity::IsPermanent() const {
-  return true;
-}
-
-}  // namespace fake_server
diff --git a/components/sync/test/fake_server/permanent_entity.h b/components/sync/test/fake_server/permanent_entity.h
deleted file mode 100644
index 735cd2c5..0000000
--- a/components/sync/test/fake_server/permanent_entity.h
+++ /dev/null
@@ -1,63 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef COMPONENTS_SYNC_TEST_FAKE_SERVER_PERMANENT_ENTITY_H_
-#define COMPONENTS_SYNC_TEST_FAKE_SERVER_PERMANENT_ENTITY_H_
-
-#include <memory>
-#include <string>
-
-#include "components/sync/base/model_type.h"
-#include "components/sync/protocol/sync.pb.h"
-#include "components/sync/test/fake_server/fake_server_entity.h"
-
-namespace fake_server {
-
-// A server-created, permanent entity.
-class PermanentEntity : public FakeServerEntity {
- public:
-  ~PermanentEntity() override;
-
-  // Factory function for PermanentEntity. |server_tag| should be a globally
-  // unique identifier.
-  static std::unique_ptr<FakeServerEntity> Create(
-      const syncer::ModelType& model_type,
-      const std::string& server_tag,
-      const std::string& name,
-      const std::string& parent_server_tag);
-
-  // Factory function for a top level PermanentEntity. Top level means that the
-  // entity's parent is the root entity (no PermanentEntity exists for root).
-  static std::unique_ptr<FakeServerEntity> CreateTopLevel(
-      const syncer::ModelType& model_type);
-
-  // Factory function for creating an updated version of a PermanentEntity.
-  // This function should only be called for the Nigori entity.
-  static std::unique_ptr<FakeServerEntity> CreateUpdatedNigoriEntity(
-      const sync_pb::SyncEntity& client_entity,
-      const FakeServerEntity& current_server_entity);
-
-  // FakeServerEntity implementation.
-  bool RequiresParentId() const override;
-  std::string GetParentId() const override;
-  void SerializeAsProto(sync_pb::SyncEntity* proto) const override;
-  bool IsFolder() const override;
-  bool IsPermanent() const override;
-
- private:
-  PermanentEntity(const std::string& id,
-                  const syncer::ModelType& model_type,
-                  const std::string& name,
-                  const std::string& parent_id,
-                  const std::string& server_defined_unique_tag,
-                  const sync_pb::EntitySpecifics& entity_specifics);
-
-  // All member values have equivalent fields in SyncEntity.
-  std::string server_defined_unique_tag_;
-  std::string parent_id_;
-};
-
-}  // namespace fake_server
-
-#endif  // COMPONENTS_SYNC_TEST_FAKE_SERVER_PERMANENT_ENTITY_H_
diff --git a/components/sync/test/fake_server/tombstone_entity.cc b/components/sync/test/fake_server/tombstone_entity.cc
deleted file mode 100644
index 014fca1..0000000
--- a/components/sync/test/fake_server/tombstone_entity.cc
+++ /dev/null
@@ -1,49 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/sync/test/fake_server/tombstone_entity.h"
-
-using std::string;
-using syncer::ModelType;
-
-namespace fake_server {
-
-TombstoneEntity::~TombstoneEntity() {}
-
-// static
-std::unique_ptr<FakeServerEntity> TombstoneEntity::Create(
-    const string& id,
-    const string& client_defined_unique_tag) {
-  const ModelType model_type = GetModelTypeFromId(id);
-  CHECK_NE(model_type, syncer::UNSPECIFIED) << "Invalid ID was given: " << id;
-  return std::unique_ptr<FakeServerEntity>(
-      new TombstoneEntity(id, client_defined_unique_tag, model_type));
-}
-
-TombstoneEntity::TombstoneEntity(const string& id,
-                                 const string& client_defined_unique_tag,
-                                 const ModelType& model_type)
-    : FakeServerEntity(id, client_defined_unique_tag, model_type, 0, string()) {
-  sync_pb::EntitySpecifics specifics;
-  AddDefaultFieldValue(model_type, &specifics);
-  SetSpecifics(specifics);
-}
-
-bool TombstoneEntity::RequiresParentId() const {
-  return false;
-}
-
-string TombstoneEntity::GetParentId() const {
-  return string();
-}
-
-void TombstoneEntity::SerializeAsProto(sync_pb::SyncEntity* proto) const {
-  FakeServerEntity::SerializeBaseProtoFields(proto);
-}
-
-bool TombstoneEntity::IsDeleted() const {
-  return true;
-}
-
-}  // namespace fake_server
diff --git a/components/sync/test/fake_server/tombstone_entity.h b/components/sync/test/fake_server/tombstone_entity.h
deleted file mode 100644
index 3ddb94d..0000000
--- a/components/sync/test/fake_server/tombstone_entity.h
+++ /dev/null
@@ -1,41 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef COMPONENTS_SYNC_TEST_FAKE_SERVER_TOMBSTONE_ENTITY_H_
-#define COMPONENTS_SYNC_TEST_FAKE_SERVER_TOMBSTONE_ENTITY_H_
-
-#include <memory>
-#include <string>
-
-#include "components/sync/base/model_type.h"
-#include "components/sync/protocol/sync.pb.h"
-#include "components/sync/test/fake_server/fake_server_entity.h"
-
-namespace fake_server {
-
-// A Sync entity that represents a deleted item.
-class TombstoneEntity : public FakeServerEntity {
- public:
-  ~TombstoneEntity() override;
-
-  // Factory function for TombstoneEntity.
-  static std::unique_ptr<FakeServerEntity> Create(
-      const std::string& id,
-      const std::string& client_defined_unique_tag);
-
-  // FakeServerEntity implementation.
-  bool RequiresParentId() const override;
-  std::string GetParentId() const override;
-  void SerializeAsProto(sync_pb::SyncEntity* proto) const override;
-  bool IsDeleted() const override;
-
- private:
-  TombstoneEntity(const std::string& id,
-                  const std::string& client_defined_unique_tag,
-                  const syncer::ModelType& model_type);
-};
-
-}  // namespace fake_server
-
-#endif  // COMPONENTS_SYNC_TEST_FAKE_SERVER_TOMBSTONE_ENTITY_H_
diff --git a/components/sync/test/fake_server/unique_client_entity.cc b/components/sync/test/fake_server/unique_client_entity.cc
deleted file mode 100644
index b090d64..0000000
--- a/components/sync/test/fake_server/unique_client_entity.cc
+++ /dev/null
@@ -1,104 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/sync/test/fake_server/unique_client_entity.h"
-
-#include "base/guid.h"
-#include "base/rand_util.h"
-#include "base/strings/string_number_conversions.h"
-#include "components/sync/base/hash_util.h"
-#include "components/sync/protocol/sync.pb.h"
-#include "components/sync/test/fake_server/permanent_entity.h"
-
-using std::string;
-using syncer::GenerateSyncableHash;
-using syncer::GetModelTypeFromSpecifics;
-using syncer::ModelType;
-
-namespace fake_server {
-
-namespace {
-
-// A version must be passed when creating a FakeServerEntity, but this value
-// is overrideen immediately when saving the entity in FakeServer.
-const int64_t kUnusedVersion = 0L;
-
-// Default time (creation and last modified) used when creating entities.
-const int64_t kDefaultTime = 1234L;
-
-}  // namespace
-
-UniqueClientEntity::UniqueClientEntity(
-    const string& id,
-    const string& client_defined_unique_tag,
-    ModelType model_type,
-    int64_t version,
-    const string& name,
-    const sync_pb::EntitySpecifics& specifics,
-    int64_t creation_time,
-    int64_t last_modified_time)
-    : FakeServerEntity(id,
-                       client_defined_unique_tag,
-                       model_type,
-                       version,
-                       name),
-      creation_time_(creation_time),
-      last_modified_time_(last_modified_time) {
-  SetSpecifics(specifics);
-}
-
-UniqueClientEntity::~UniqueClientEntity() {}
-
-// static
-std::unique_ptr<FakeServerEntity> UniqueClientEntity::Create(
-    const sync_pb::SyncEntity& client_entity) {
-  ModelType model_type =
-      syncer::GetModelTypeFromSpecifics(client_entity.specifics());
-  CHECK_NE(client_entity.has_client_defined_unique_tag(),
-           syncer::CommitOnlyTypes().Has(model_type))
-      << "A UniqueClientEntity should have a client-defined unique tag iff it "
-         "is not a CommitOnly type.";
-  // Without model type specific logic for each CommitOnly type, we cannot infer
-  // a reasonable tag from the specifics. We need uniqueness for how the server
-  // holds onto all objects, so simply make a new tag from a random  number.
-  string effective_tag = client_entity.has_client_defined_unique_tag()
-                             ? client_entity.client_defined_unique_tag()
-                             : base::Uint64ToString(base::RandUint64());
-  string id = FakeServerEntity::CreateId(model_type, effective_tag);
-  return std::unique_ptr<FakeServerEntity>(new UniqueClientEntity(
-      id, effective_tag, model_type, client_entity.version(),
-      client_entity.name(), client_entity.specifics(), client_entity.ctime(),
-      client_entity.mtime()));
-}
-
-// static
-std::unique_ptr<FakeServerEntity> UniqueClientEntity::CreateForInjection(
-    const string& name,
-    const sync_pb::EntitySpecifics& entity_specifics) {
-  ModelType model_type = GetModelTypeFromSpecifics(entity_specifics);
-  string client_defined_unique_tag = GenerateSyncableHash(model_type, name);
-  string id = FakeServerEntity::CreateId(model_type, client_defined_unique_tag);
-  return std::unique_ptr<FakeServerEntity>(new UniqueClientEntity(
-      id, client_defined_unique_tag, model_type, kUnusedVersion, name,
-      entity_specifics, kDefaultTime, kDefaultTime));
-}
-
-bool UniqueClientEntity::RequiresParentId() const {
-  return false;
-}
-
-string UniqueClientEntity::GetParentId() const {
-  // The parent ID for this type of entity should always be its ModelType's
-  // root node.
-  return FakeServerEntity::GetTopLevelId(model_type());
-}
-
-void UniqueClientEntity::SerializeAsProto(sync_pb::SyncEntity* proto) const {
-  FakeServerEntity::SerializeBaseProtoFields(proto);
-
-  proto->set_ctime(creation_time_);
-  proto->set_mtime(last_modified_time_);
-}
-
-}  // namespace fake_server
diff --git a/components/sync/test/fake_server/unique_client_entity.h b/components/sync/test/fake_server/unique_client_entity.h
deleted file mode 100644
index c9c42453..0000000
--- a/components/sync/test/fake_server/unique_client_entity.h
+++ /dev/null
@@ -1,64 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef COMPONENTS_SYNC_TEST_FAKE_SERVER_UNIQUE_CLIENT_ENTITY_H_
-#define COMPONENTS_SYNC_TEST_FAKE_SERVER_UNIQUE_CLIENT_ENTITY_H_
-
-#include <stdint.h>
-
-#include <memory>
-#include <string>
-
-#include "components/sync/base/model_type.h"
-#include "components/sync/test/fake_server/fake_server_entity.h"
-
-namespace sync_pb {
-class EntitySpecifics;
-class SyncEntity;
-}  // namespace sync_pb
-
-namespace fake_server {
-
-// An entity that is unique per client account.
-//
-// TODO(pvalenzuela): Reconsider the naming of this class. In some cases, this
-// type is used to represent entities where the unique client tag is irrelevant
-// (e.g., Autofill Wallet).
-class UniqueClientEntity : public FakeServerEntity {
- public:
-  UniqueClientEntity(const std::string& id,
-                     const std::string& client_defined_unique_tag,
-                     syncer::ModelType model_type,
-                     int64_t version,
-                     const std::string& name,
-                     const sync_pb::EntitySpecifics& specifics,
-                     int64_t creation_time,
-                     int64_t last_modified_time);
-
-  ~UniqueClientEntity() override;
-
-  // Factory function for creating a UniqueClientEntity.
-  static std::unique_ptr<FakeServerEntity> Create(
-      const sync_pb::SyncEntity& client_entity);
-
-  // Factory function for creating a UniqueClientEntity for use in the
-  // FakeServer injection API.
-  static std::unique_ptr<FakeServerEntity> CreateForInjection(
-      const std::string& name,
-      const sync_pb::EntitySpecifics& entity_specifics);
-
-  // FakeServerEntity implementation.
-  bool RequiresParentId() const override;
-  std::string GetParentId() const override;
-  void SerializeAsProto(sync_pb::SyncEntity* proto) const override;
-
- private:
-  // These member values have equivalent fields in SyncEntity.
-  int64_t creation_time_;
-  int64_t last_modified_time_;
-};
-
-}  // namespace fake_server
-
-#endif  // COMPONENTS_SYNC_TEST_FAKE_SERVER_UNIQUE_CLIENT_ENTITY_H_
diff --git a/content/browser/frame_host/render_widget_host_view_guest.cc b/content/browser/frame_host/render_widget_host_view_guest.cc
index a3f8cd8..736645b 100644
--- a/content/browser/frame_host/render_widget_host_view_guest.cc
+++ b/content/browser/frame_host/render_widget_host_view_guest.cc
@@ -265,6 +265,16 @@
    platform_view_->SetNeedsBeginFrames(needs_begin_frames);
 }
 
+TouchSelectionControllerClientManager*
+RenderWidgetHostViewGuest::GetTouchSelectionControllerClientManager() {
+  RenderWidgetHostView* root_view = GetOwnerRenderWidgetHostView();
+  if (!root_view)
+    return nullptr;
+
+  // There is only ever one manager, and it's owned by the root view.
+  return root_view->GetTouchSelectionControllerClientManager();
+}
+
 void RenderWidgetHostViewGuest::SetTooltipText(
     const base::string16& tooltip_text) {
   if (guest_)
diff --git a/content/browser/frame_host/render_widget_host_view_guest.h b/content/browser/frame_host/render_widget_host_view_guest.h
index f11f219..1720632 100644
--- a/content/browser/frame_host/render_widget_host_view_guest.h
+++ b/content/browser/frame_host/render_widget_host_view_guest.h
@@ -69,6 +69,8 @@
   gfx::Size GetPhysicalBackingSize() const override;
   base::string16 GetSelectedText() override;
   void SetNeedsBeginFrames(bool needs_begin_frames) override;
+  TouchSelectionControllerClientManager*
+  GetTouchSelectionControllerClientManager() override;
 
   // RenderWidgetHostViewBase implementation.
   void InitAsPopup(RenderWidgetHostView* parent_host_view,
diff --git a/content/browser/indexed_db/indexed_db_internals_ui.cc b/content/browser/indexed_db/indexed_db_internals_ui.cc
index 6ab7260..65a7916 100644
--- a/content/browser/indexed_db/indexed_db_internals_ui.cc
+++ b/content/browser/indexed_db/indexed_db_internals_ui.cc
@@ -281,9 +281,29 @@
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
   const GURL url = GURL(FILE_PATH_LITERAL("file://") + zip_path.value());
   WebContents* web_contents = web_ui()->GetWebContents();
+  net::NetworkTrafficAnnotationTag traffic_annotation =
+      net::DefineNetworkTrafficAnnotation("indexed_db_internals_handler", R"(
+        semantics {
+          sender: "Indexed DB Internals"
+          description:
+            "This is an internal Chrome webpage that displays debug "
+            "information about IndexedDB usage and data, used by developers."
+          trigger: "When a user navigates to chrome://indexeddb-internals/."
+          data: "None."
+          destination: LOCAL
+        }
+        policy {
+          cookies_allowed: false
+          setting:
+            "This feature cannot be disabled by settings, but it's only "
+            "triggered by navigating to the specified URL."
+          policy_exception_justification:
+            "Not implemented. Indexed DB is Chrome's internal local data "
+            "storage.
+        })");
   std::unique_ptr<DownloadUrlParameters> dl_params(
-      DownloadUrlParameters::CreateForWebContentsMainFrame(
-          web_contents, url, NO_TRAFFIC_ANNOTATION_YET));
+      DownloadUrlParameters::CreateForWebContentsMainFrame(web_contents, url,
+                                                           traffic_annotation));
   const GURL referrer(web_contents->GetLastCommittedURL());
   dl_params->set_referrer(content::Referrer::SanitizeForRequest(
       url, content::Referrer(referrer, blink::kWebReferrerPolicyDefault)));
diff --git a/content/browser/web_contents/web_contents_impl.cc b/content/browser/web_contents/web_contents_impl.cc
index cb077e5bd..c2854649 100644
--- a/content/browser/web_contents/web_contents_impl.cc
+++ b/content/browser/web_contents/web_contents_impl.cc
@@ -3241,11 +3241,31 @@
     if (entry)
       post_id = entry->GetPostID();
   }
+  net::NetworkTrafficAnnotationTag traffic_annotation =
+      net::DefineNetworkTrafficAnnotation("download_web_contents_frame", R"(
+        semantics {
+          sender: "Save Page Action"
+          description:
+            "Saves the given frame's URL to the local file system."
+          trigger:
+            "The user has triggered a save operation on the frame through a "
+            "context menu or other mechanism."
+          data: "None."
+          destination: WEBSITE
+        }
+        policy {
+          cookies_allowed: true
+          cookies_store: "user"
+          setting:
+            "This feature cannot be disabled by settings, but it's is only "
+            "triggered by user request."
+          policy_exception_justification: "Not implemented."
+        })");
   auto params = base::MakeUnique<DownloadUrlParameters>(
       url, frame_host->GetProcess()->GetID(),
       frame_host->GetRenderViewHost()->GetRoutingID(),
       frame_host->GetRoutingID(), storage_partition->GetURLRequestContext(),
-      NO_TRAFFIC_ANNOTATION_YET);
+      traffic_annotation);
   params->set_referrer(referrer);
   params->set_post_id(post_id);
   if (post_id >= 0)
diff --git a/content/renderer/media/rtc_peer_connection_handler.cc b/content/renderer/media/rtc_peer_connection_handler.cc
index 21792a5..1fb95e1 100644
--- a/content/renderer/media/rtc_peer_connection_handler.cc
+++ b/content/renderer/media/rtc_peer_connection_handler.cc
@@ -1698,6 +1698,43 @@
   return result;
 }
 
+std::unique_ptr<blink::WebRTCRtpSender> RTCPeerConnectionHandler::AddTrack(
+    const blink::WebMediaStreamTrack& track,
+    const blink::WebVector<blink::WebMediaStream>& streams) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+
+  // Get or create the associated track and stream adapters.
+  std::unique_ptr<WebRtcMediaStreamTrackAdapterMap::AdapterRef> track_adapter =
+      track_adapter_map_->GetOrCreateLocalTrackAdapter(track);
+  std::vector<std::unique_ptr<WebRtcMediaStreamAdapterMap::AdapterRef>>
+      stream_adapters(streams.size());
+  std::vector<webrtc::MediaStreamInterface*> webrtc_streams(streams.size());
+  for (size_t i = 0; i < streams.size(); ++i) {
+    stream_adapters[i] =
+        stream_adapter_map_->GetOrCreateLocalStreamAdapter(streams[i]);
+    webrtc_streams[i] = stream_adapters[i]->adapter().webrtc_media_stream();
+  }
+
+  rtc::scoped_refptr<webrtc::RtpSenderInterface> webrtc_sender =
+      native_peer_connection_->AddTrack(track_adapter->webrtc_track(),
+                                        webrtc_streams);
+  if (!webrtc_sender)
+    return nullptr;
+  return base::MakeUnique<RTCRtpSender>(std::move(webrtc_sender),
+                                        std::move(track_adapter),
+                                        std::move(stream_adapters));
+}
+
+bool RTCPeerConnectionHandler::RemoveTrack(blink::WebRTCRtpSender* web_sender) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  RTCRtpSender* sender = static_cast<RTCRtpSender*>(web_sender);
+  if (!native_peer_connection_->RemoveTrack(sender->webrtc_rtp_sender())) {
+    return false;
+  }
+  sender->OnRemoved();
+  return true;
+}
+
 void RTCPeerConnectionHandler::CloseClientPeerConnection() {
   DCHECK(thread_checker_.CalledOnValidThread());
   if (!is_closed_)
diff --git a/content/renderer/media/rtc_peer_connection_handler.h b/content/renderer/media/rtc_peer_connection_handler.h
index 89bcb95..5b7434c 100644
--- a/content/renderer/media/rtc_peer_connection_handler.h
+++ b/content/renderer/media/rtc_peer_connection_handler.h
@@ -152,6 +152,11 @@
       override;
   blink::WebVector<std::unique_ptr<blink::WebRTCRtpReceiver>> GetReceivers()
       override;
+  std::unique_ptr<blink::WebRTCRtpSender> AddTrack(
+      const blink::WebMediaStreamTrack& web_track,
+      const blink::WebVector<blink::WebMediaStream>& web_streams) override;
+  bool RemoveTrack(blink::WebRTCRtpSender* web_sender) override;
+
   blink::WebRTCDataChannelHandler* CreateDataChannel(
       const blink::WebString& label,
       const blink::WebRTCDataChannelInit& init) override;
diff --git a/content/renderer/media/webrtc/rtc_rtp_sender.cc b/content/renderer/media/webrtc/rtc_rtp_sender.cc
index 26b15b8..0124448 100644
--- a/content/renderer/media/webrtc/rtc_rtp_sender.cc
+++ b/content/renderer/media/webrtc/rtc_rtp_sender.cc
@@ -5,7 +5,6 @@
 #include "content/renderer/media/webrtc/rtc_rtp_sender.h"
 
 #include "base/logging.h"
-#include "third_party/webrtc/rtc_base/scoped_ref_ptr.h"
 
 namespace content {
 
@@ -15,8 +14,13 @@
     const std::unique_ptr<WebRtcMediaStreamTrackAdapterMap::AdapterRef>&
         track_adapter,
     const webrtc::MediaStreamTrackInterface* webrtc_track) {
-  if (!track_adapter)
-    return !webrtc_track;
+  if (!track_adapter) {
+    // TODO(hbos): See TODO in |OnRemoved|. Because we can't set a stopped
+    // sender's track to null any |webrtc_track| could be correct. When this is
+    // fixed this line should be: return !webrtc_track;
+    // https://crbug.com/webrtc/7945
+    return true;
+  }
   return track_adapter->webrtc_track() == webrtc_track;
 }
 
@@ -28,10 +32,22 @@
 }
 
 RTCRtpSender::RTCRtpSender(
-    scoped_refptr<webrtc::RtpSenderInterface> webrtc_rtp_sender,
+    rtc::scoped_refptr<webrtc::RtpSenderInterface> webrtc_rtp_sender,
     std::unique_ptr<WebRtcMediaStreamTrackAdapterMap::AdapterRef> track_adapter)
+    : RTCRtpSender(
+          std::move(webrtc_rtp_sender),
+          std::move(track_adapter),
+          std::vector<
+              std::unique_ptr<WebRtcMediaStreamAdapterMap::AdapterRef>>()) {}
+
+RTCRtpSender::RTCRtpSender(
+    rtc::scoped_refptr<webrtc::RtpSenderInterface> webrtc_rtp_sender,
+    std::unique_ptr<WebRtcMediaStreamTrackAdapterMap::AdapterRef> track_adapter,
+    std::vector<std::unique_ptr<WebRtcMediaStreamAdapterMap::AdapterRef>>
+        stream_adapters)
     : webrtc_rtp_sender_(std::move(webrtc_rtp_sender)),
-      track_adapter_(std::move(track_adapter)) {
+      track_adapter_(std::move(track_adapter)),
+      stream_adapters_(std::move(stream_adapters)) {
   DCHECK(webrtc_rtp_sender_);
   DCHECK(track_adapter_ == webrtc_rtp_sender_->track());
 }
@@ -47,6 +63,16 @@
   return track_adapter_ ? &track_adapter_->web_track() : nullptr;
 }
 
+void RTCRtpSender::OnRemoved() {
+  // TODO(hbos): We should do |webrtc_rtp_sender_->SetTrack(null)| but that is
+  // not allowed on a stopped sender. https://crbug.com/webrtc/7945
+  track_adapter_.reset();
+}
+
+webrtc::RtpSenderInterface* RTCRtpSender::webrtc_rtp_sender() {
+  return webrtc_rtp_sender_.get();
+}
+
 const webrtc::MediaStreamTrackInterface* RTCRtpSender::webrtc_track() const {
   DCHECK(track_adapter_ == webrtc_rtp_sender_->track());
   return track_adapter_ ? track_adapter_->webrtc_track() : nullptr;
diff --git a/content/renderer/media/webrtc/rtc_rtp_sender.h b/content/renderer/media/webrtc/rtc_rtp_sender.h
index 0a375601..3205abe 100644
--- a/content/renderer/media/webrtc/rtc_rtp_sender.h
+++ b/content/renderer/media/webrtc/rtc_rtp_sender.h
@@ -5,12 +5,16 @@
 #ifndef CONTENT_RENDERER_MEDIA_WEBRTC_RTC_RTP_SENDER_H_
 #define CONTENT_RENDERER_MEDIA_WEBRTC_RTC_RTP_SENDER_H_
 
-#include "base/memory/ref_counted.h"
+#include <memory>
+#include <vector>
+
 #include "content/common/content_export.h"
+#include "content/renderer/media/webrtc/webrtc_media_stream_adapter_map.h"
 #include "content/renderer/media/webrtc/webrtc_media_stream_track_adapter_map.h"
 #include "third_party/WebKit/public/platform/WebMediaStreamTrack.h"
 #include "third_party/WebKit/public/platform/WebRTCRtpSender.h"
 #include "third_party/webrtc/api/rtpsenderinterface.h"
+#include "third_party/webrtc/rtc_base/scoped_ref_ptr.h"
 
 namespace content {
 
@@ -21,22 +25,34 @@
  public:
   static uintptr_t getId(const webrtc::RtpSenderInterface* webrtc_rtp_sender);
 
-  RTCRtpSender(scoped_refptr<webrtc::RtpSenderInterface> webrtc_rtp_sender,
+  RTCRtpSender(rtc::scoped_refptr<webrtc::RtpSenderInterface> webrtc_rtp_sender,
                std::unique_ptr<WebRtcMediaStreamTrackAdapterMap::AdapterRef>
                    track_adapter);
+  RTCRtpSender(
+      rtc::scoped_refptr<webrtc::RtpSenderInterface> webrtc_rtp_sender,
+      std::unique_ptr<WebRtcMediaStreamTrackAdapterMap::AdapterRef>
+          track_adapter,
+      std::vector<std::unique_ptr<WebRtcMediaStreamAdapterMap::AdapterRef>>
+          stream_adapters);
   ~RTCRtpSender() override;
 
   uintptr_t Id() const override;
   const blink::WebMediaStreamTrack* Track() const override;
 
+  void OnRemoved();
+  webrtc::RtpSenderInterface* webrtc_rtp_sender();
   const webrtc::MediaStreamTrackInterface* webrtc_track() const;
 
  private:
-  const scoped_refptr<webrtc::RtpSenderInterface> webrtc_rtp_sender_;
+  const rtc::scoped_refptr<webrtc::RtpSenderInterface> webrtc_rtp_sender_;
   // The track adapter is the glue between blink and webrtc layer tracks.
   // Keeping a reference to the adapter ensures it is not disposed, as is
   // required as long as the webrtc layer track is in use by the sender.
   std::unique_ptr<WebRtcMediaStreamTrackAdapterMap::AdapterRef> track_adapter_;
+  // Similarly, reference needs to be kept to the stream adapters when a sender
+  // is created for |addTrack| with associated stream(s).
+  std::vector<std::unique_ptr<WebRtcMediaStreamAdapterMap::AdapterRef>>
+      stream_adapters_;
 };
 
 }  // namespace content
diff --git a/content/shell/test_runner/mock_webrtc_peer_connection_handler.cc b/content/shell/test_runner/mock_webrtc_peer_connection_handler.cc
index 61082718..8e5512be 100644
--- a/content/shell/test_runner/mock_webrtc_peer_connection_handler.cc
+++ b/content/shell/test_runner/mock_webrtc_peer_connection_handler.cc
@@ -251,6 +251,10 @@
     return track_.get();
   }
 
+  void SetTrack(std::unique_ptr<blink::WebMediaStreamTrack> track) {
+    track_ = std::move(track);
+  }
+
  private:
   uintptr_t id_;
   std::unique_ptr<blink::WebMediaStreamTrack> track_;
@@ -638,6 +642,7 @@
 blink::WebVector<std::unique_ptr<blink::WebRTCRtpSender>>
 MockWebRTCPeerConnectionHandler::GetSenders() {
   std::vector<std::unique_ptr<blink::WebRTCRtpSender>> senders;
+  // Senders of tracks in |local_streams_| (from |Add/RemoveStream|).
   for (const auto& pair : local_streams_) {
     const auto& local_stream = pair.second;
     blink::WebVector<blink::WebMediaStreamTrack> local_tracks;
@@ -656,6 +661,22 @@
               base::MakeUnique<WebMediaStreamTrack>(local_track))));
     }
   }
+  // Senders of tracks in |tracks_| (from |Add/RemoveTrack|).
+  for (const auto& pair : tracks_) {
+    const auto& track = pair.second;
+    bool has_sender_for_track = false;
+    for (const auto& sender : senders) {
+      if (sender->Track()->Id() == track.Id()) {
+        has_sender_for_track = true;
+        break;
+      }
+    }
+    if (has_sender_for_track)
+      continue;
+    senders.push_back(base::MakeUnique<MockWebRTCRtpSender>(
+        GetIDByTrack(track.Id().Utf8(), &id_by_track_),
+        base::MakeUnique<WebMediaStreamTrack>(track)));
+  }
   blink::WebVector<std::unique_ptr<blink::WebRTCRtpSender>> web_vector(
       senders.size());
   for (size_t i = 0; i < senders.size(); ++i) {
@@ -693,6 +714,32 @@
   return web_vector;
 }
 
+std::unique_ptr<blink::WebRTCRtpSender>
+MockWebRTCPeerConnectionHandler::AddTrack(
+    const blink::WebMediaStreamTrack& web_track,
+    const blink::WebVector<blink::WebMediaStream>& web_streams) {
+  for (const auto& sender : GetSenders()) {
+    if (sender->Track() && sender->Track()->Id() == web_track.Id()) {
+      return nullptr;
+    }
+  }
+  tracks_[web_track.Id().Utf8()] = web_track;
+  client_->NegotiationNeeded();
+  return base::MakeUnique<MockWebRTCRtpSender>(
+      GetIDByTrack(web_track.Id().Utf8(), &id_by_track_),
+      base::MakeUnique<blink::WebMediaStreamTrack>(web_track));
+}
+
+bool MockWebRTCPeerConnectionHandler::RemoveTrack(
+    blink::WebRTCRtpSender* web_sender) {
+  if (!tracks_.erase(web_sender->Track()->Id().Utf8()))
+    return false;
+  MockWebRTCRtpSender* sender = static_cast<MockWebRTCRtpSender*>(web_sender);
+  sender->SetTrack(nullptr);
+  client_->NegotiationNeeded();
+  return true;
+}
+
 void MockWebRTCPeerConnectionHandler::ReportCreationOfDataChannel() {
   WebRTCDataChannelInit init;
   WebRTCDataChannelHandler* remote_data_channel =
diff --git a/content/shell/test_runner/mock_webrtc_peer_connection_handler.h b/content/shell/test_runner/mock_webrtc_peer_connection_handler.h
index 415bee21..1b76170933 100644
--- a/content/shell/test_runner/mock_webrtc_peer_connection_handler.h
+++ b/content/shell/test_runner/mock_webrtc_peer_connection_handler.h
@@ -65,6 +65,10 @@
       override;
   blink::WebVector<std::unique_ptr<blink::WebRTCRtpReceiver>> GetReceivers()
       override;
+  std::unique_ptr<blink::WebRTCRtpSender> AddTrack(
+      const blink::WebMediaStreamTrack& web_track,
+      const blink::WebVector<blink::WebMediaStream>& web_streams) override;
+  bool RemoveTrack(blink::WebRTCRtpSender* web_sender) override;
   blink::WebRTCDataChannelHandler* CreateDataChannel(
       const blink::WebString& label,
       const blink::WebRTCDataChannelInit& init) override;
@@ -101,6 +105,8 @@
   typedef std::map<std::string, blink::WebMediaStream> StreamMap;
   StreamMap local_streams_;
   StreamMap remote_streams_;
+  // Tracks added with |addTrack|, not including tracks of |local_streams_|.
+  std::map<std::string, blink::WebMediaStreamTrack> tracks_;
   std::map<std::string, uintptr_t> id_by_track_;
 
   base::WeakPtrFactory<MockWebRTCPeerConnectionHandler> weak_factory_;
diff --git a/headless/lib/browser/headless_browser_main_parts.cc b/headless/lib/browser/headless_browser_main_parts.cc
index d69471e..64e9018 100644
--- a/headless/lib/browser/headless_browser_main_parts.cc
+++ b/headless/lib/browser/headless_browser_main_parts.cc
@@ -27,6 +27,8 @@
     base::FilePath log_path =
         command_line->GetSwitchValuePath(switches::kLogNetLog);
     net_log_.reset(new HeadlessNetLog(log_path));
+  } else {
+    net_log_.reset(new net::NetLog());
   }
 
   if (browser_->options()->DevtoolsServerEnabled()) {
diff --git a/headless/lib/browser/headless_browser_main_parts.h b/headless/lib/browser/headless_browser_main_parts.h
index 0b8d81f..78ee270 100644
--- a/headless/lib/browser/headless_browser_main_parts.h
+++ b/headless/lib/browser/headless_browser_main_parts.h
@@ -11,9 +11,12 @@
 #include "content/public/browser/browser_main_parts.h"
 #include "headless/public/headless_browser.h"
 
+namespace net {
+class NetLog;
+}  // namespace net
+
 namespace headless {
 
-class HeadlessNetLog;
 class HeadlessBrowserImpl;
 
 class HeadlessBrowserMainParts : public content::BrowserMainParts {
@@ -28,13 +31,13 @@
   void PreMainMessageLoopStart() override;
 #endif
 
-  HeadlessNetLog* net_log() const { return net_log_.get(); }
+  net::NetLog* net_log() const { return net_log_.get(); }
 
  private:
   HeadlessBrowserImpl* browser_;  // Not owned.
 
   bool devtools_http_handler_started_;
-  std::unique_ptr<HeadlessNetLog> net_log_;
+  std::unique_ptr<net::NetLog> net_log_;
 
   DISALLOW_COPY_AND_ASSIGN(HeadlessBrowserMainParts);
 };
diff --git a/headless/lib/browser/headless_content_browser_client.cc b/headless/lib/browser/headless_content_browser_client.cc
index 2f2cc780..5001094 100644
--- a/headless/lib/browser/headless_content_browser_client.cc
+++ b/headless/lib/browser/headless_content_browser_client.cc
@@ -239,4 +239,8 @@
       resource_dispatcher_host_delegate_.get());
 }
 
+net::NetLog* HeadlessContentBrowserClient::GetNetLog() {
+  return browser_->browser_main_parts()->net_log();
+}
+
 }  // namespace headless
diff --git a/headless/lib/browser/headless_content_browser_client.h b/headless/lib/browser/headless_content_browser_client.h
index 7bb377f..4bd0934 100644
--- a/headless/lib/browser/headless_content_browser_client.h
+++ b/headless/lib/browser/headless_content_browser_client.h
@@ -52,6 +52,8 @@
 
   void ResourceDispatcherHostCreated() override;
 
+  net::NetLog* GetNetLog() override;
+
  private:
   std::unique_ptr<base::Value> GetBrowserServiceManifestOverlay();
   std::unique_ptr<base::Value> GetRendererServiceManifestOverlay();
diff --git a/headless/lib/headless_devtools_client_browsertest.cc b/headless/lib/headless_devtools_client_browsertest.cc
index 5d97e60e..5e3181e4 100644
--- a/headless/lib/headless_devtools_client_browsertest.cc
+++ b/headless/lib/headless_devtools_client_browsertest.cc
@@ -1438,4 +1438,31 @@
 
 HEADLESS_ASYNC_DEVTOOLED_TEST_F(FailedUrlRequestTest);
 
+class DevToolsSetCookieTest : public HeadlessAsyncDevTooledBrowserTest,
+                              public network::Observer {
+ public:
+  void RunDevTooledTest() override {
+    EXPECT_TRUE(embedded_test_server()->Start());
+    devtools_client_->GetNetwork()->AddObserver(this);
+
+    base::RunLoop run_loop;
+    devtools_client_->GetNetwork()->Enable(run_loop.QuitClosure());
+    base::MessageLoop::ScopedNestableTaskAllower nest_loop(
+        base::MessageLoop::current());
+    run_loop.Run();
+
+    devtools_client_->GetPage()->Navigate(
+        embedded_test_server()->GetURL("/set-cookie?cookie1").spec());
+  }
+
+  void OnResponseReceived(
+      const network::ResponseReceivedParams& params) override {
+    EXPECT_NE(std::string::npos, params.GetResponse()->GetHeadersText().find(
+                                     "Set-Cookie: cookie1"));
+    FinishAsynchronousTest();
+  }
+};
+
+HEADLESS_ASYNC_DEVTOOLED_TEST_F(DevToolsSetCookieTest);
+
 }  // namespace headless
diff --git a/ios/build/bots/chromium.mac/ios-simulator-cronet.json b/ios/build/bots/chromium.mac/ios-simulator-cronet.json
index 7dd030a..0ae0e8e 100644
--- a/ios/build/bots/chromium.mac/ios-simulator-cronet.json
+++ b/ios/build/bots/chromium.mac/ios-simulator-cronet.json
@@ -6,7 +6,7 @@
   "xcode version": "8.0",
   "gn_args": [
     "additional_target_cpus=[\"x86\"]",
-    "disable_brotli_filter=true",
+    "disable_brotli_filter=false",
     "disable_file_support=true",
     "disable_ftp_support=true",
     "enable_websockets=false",
diff --git a/ios/chrome/app/strings/ios_strings.grd b/ios/chrome/app/strings/ios_strings.grd
index 812312d3..0473879 100644
--- a/ios/chrome/app/strings/ios_strings.grd
+++ b/ios/chrome/app/strings/ios_strings.grd
@@ -1122,12 +1122,21 @@
       <message name="IDS_IOS_SETTINGS_SITE_COPY_BUTTON" desc="Button that the user can press to copy the site to clipboard. [Length: 12em]">
         Copy
       </message>
+      <message name="IDS_IOS_SETTINGS_SITE_COPY_MENU_ITEM" desc="Context menu item available on the cell showing the site/URL with a passwords origin, in the detailed view of a stored credential. The user can select it to copy the site to clipboard. [Length: should be one short word, is under 5em in English]">
+        Copy
+      </message>
       <message name="IDS_IOS_SETTINGS_USERNAME_COPY_BUTTON" desc="Button that the user can press to copy the username to clipboard. [Length: 12em]">
         Copy
       </message>
+      <message name="IDS_IOS_SETTINGS_USERNAME_COPY_MENU_ITEM" desc="Context menu item available on the username cell in the detailed view of a stored credential. The user can select it to copy the username to clipboard. [Length: should be one short word, is under 5em in English]">
+        Copy
+      </message>
       <message name="IDS_IOS_SETTINGS_PASSWORD_COPY_BUTTON" desc="Button that the user can press to copy the password to clipboard. [Length: 12em]">
         Copy
       </message>
+      <message name="IDS_IOS_SETTINGS_PASSWORD_COPY_MENU_ITEM" desc="Context menu item available on the password cell in detailed view of a stored credential. The user can select it to copy the password to clipboard. [Length: should be one short word, is under 5em in English]">
+        Copy
+      </message>
       <message name="IDS_IOS_SETTINGS_PASSWORD_DELETE_BUTTON" desc="Button that the user can press to delete the saved password. [Length: 31em]">
         Delete Saved Password
       </message>
@@ -1137,6 +1146,9 @@
       <message name="IDS_IOS_SETTINGS_PASSWORD_HIDE_BUTTON" desc="Button that the user can press to change the way the password is displayed from plain text to obscured form (i.e. from plain text to dots). [Length: 12em]">
         Hide
       </message>
+      <message name="IDS_IOS_SETTINGS_PASSWORD_HIDE_MENU_ITEM" desc="Context menu item available on the password cell in detailed view of a stored password. The user can select it to hide the password to clipboard. [Length: should be one short word, is under 5em in English]">
+        Hide
+      </message>
       <message name="IDS_IOS_SETTINGS_PASSWORD_REAUTH_REASON_COPY" desc="Message explaining that the reason why we are requiring the user to re-authenticate is to be able to copy the password. This is shown by iOS in the Touch ID reauthentication system dialogue, right under the OS string 'Touch ID for $BROWSER_NAME'. [Length: 22em]">
         Copy password
       </message>
@@ -1146,6 +1158,9 @@
       <message name="IDS_IOS_SETTINGS_PASSWORD_SHOW_BUTTON" desc="Button that the user can press in order to have the password displayed in plain text. [Length: 12em]">
         Show
       </message>
+      <message name="IDS_IOS_SETTINGS_PASSWORD_SHOW_MENU_ITEM" desc="Context menu item available on the password cell in detailed view of a stored password. The user can select it to show the password to clipboard. [Length: should be one short word, is under 5em in English]">
+        Show
+      </message>
       <message name="IDS_IOS_SETTINGS_SITE_WAS_COPIED_MESSAGE" desc="Confirmation that the site URL of a saved credential was copied. This is shown alone in an infobar at the bottom of the screen. [Length: 40em]">
         The site URL was copied.
       </message>
diff --git a/ios/chrome/browser/ui/settings/password_details_collection_view_controller.mm b/ios/chrome/browser/ui/settings/password_details_collection_view_controller.mm
index ac46981cf..5f1ed06 100644
--- a/ios/chrome/browser/ui/settings/password_details_collection_view_controller.mm
+++ b/ios/chrome/browser/ui/settings/password_details_collection_view_controller.mm
@@ -56,7 +56,18 @@
 
 }  // namespace
 
-@interface PasswordDetailsCollectionViewController () {
+// This protocol declares the methods used by the context menus, so that
+// selectors can be created from those methods and passed to UIMenuItem.
+@protocol PasswordDetailsViewerProtocol<NSObject>
+- (void)copySite;
+- (void)copyUsername;
+- (void)copyPassword;
+- (void)showPassword;
+- (void)hidePassword;
+@end
+
+@interface PasswordDetailsCollectionViewController ()<
+    PasswordDetailsViewerProtocol> {
   // The username to which the saved password belongs.
   NSString* _username;
   // The saved password.
@@ -446,6 +457,74 @@
   [self presentViewController:_deleteConfirmation animated:YES completion:nil];
 }
 
+// Returns an array of UIMenuItems to display in a context menu on the site
+// cell.
+- (NSArray*)getSiteMenuItems {
+  UIMenuItem* copyOption = [[UIMenuItem alloc]
+      initWithTitle:l10n_util::GetNSString(IDS_IOS_SETTINGS_SITE_COPY_MENU_ITEM)
+             action:@selector(copySite)];
+  return @[ copyOption ];
+}
+
+// Returns an array of UIMenuItems to display in a context menu on the username
+// cell.
+- (NSArray*)getUsernameMenuItems {
+  UIMenuItem* copyOption = [[UIMenuItem alloc]
+      initWithTitle:l10n_util::GetNSString(
+                        IDS_IOS_SETTINGS_USERNAME_COPY_MENU_ITEM)
+             action:@selector(copyUsername)];
+  return @[ copyOption ];
+}
+
+// Returns an array of UIMenuItems to display in a context menu on the password
+// cell.
+- (NSArray*)getPasswordMenuItems {
+  UIMenuItem* copyOption = [[UIMenuItem alloc]
+      initWithTitle:l10n_util::GetNSString(
+                        IDS_IOS_SETTINGS_PASSWORD_COPY_MENU_ITEM)
+             action:@selector(copyPassword)];
+  UIMenuItem* showOption = [[UIMenuItem alloc]
+      initWithTitle:l10n_util::GetNSString(
+                        IDS_IOS_SETTINGS_PASSWORD_SHOW_MENU_ITEM)
+             action:@selector(showPassword)];
+  UIMenuItem* hideOption = [[UIMenuItem alloc]
+      initWithTitle:l10n_util::GetNSString(
+                        IDS_IOS_SETTINGS_PASSWORD_HIDE_MENU_ITEM)
+             action:@selector(hidePassword)];
+  return @[ copyOption, showOption, hideOption ];
+}
+
+// If the context menu is not shown for a given item type, constructs that
+// menu and shows it. This method should only be called for item types
+// representing the cells with the site, username and password.
+- (void)ensureContextMenuShownForItemType:(NSInteger)itemType
+                           collectionView:(UICollectionView*)collectionView
+                              atIndexPath:(NSIndexPath*)indexPath {
+  UIMenuController* menu = [UIMenuController sharedMenuController];
+  if (![menu isMenuVisible]) {
+    NSArray* options = nil;
+    switch (itemType) {
+      case ItemTypeSite:
+        options = [self getSiteMenuItems];
+        break;
+      case ItemTypeUsername:
+        options = [self getUsernameMenuItems];
+        break;
+      case ItemTypePassword:
+        options = [self getPasswordMenuItems];
+        break;
+      default:
+        NOTREACHED();
+    }
+    [menu setMenuItems:options];
+    UICollectionViewLayoutAttributes* attributes =
+        [collectionView.collectionViewLayout
+            layoutAttributesForItemAtIndexPath:indexPath];
+    [menu setTargetRect:attributes.frame inView:collectionView];
+    [menu setMenuVisible:YES animated:YES];
+  }
+}
+
 #pragma mark - UICollectionViewDelegate
 
 - (void)collectionView:(UICollectionView*)collectionView
@@ -454,6 +533,13 @@
   NSInteger itemType =
       [self.collectionViewModel itemTypeForIndexPath:indexPath];
   switch (itemType) {
+    case ItemTypeSite:
+    case ItemTypeUsername:
+    case ItemTypePassword:
+      [self ensureContextMenuShownForItemType:itemType
+                               collectionView:collectionView
+                                  atIndexPath:indexPath];
+      break;
     case ItemTypeCopySite:
       [self copySite];
       break;
@@ -502,4 +588,20 @@
   _weakReauthenticationModule = reauthenticationModule;
 }
 
+#pragma mark - UIResponder
+
+- (BOOL)canBecomeFirstResponder {
+  return YES;
+}
+
+- (BOOL)canPerformAction:(SEL)action withSender:(id)sender {
+  if (action == @selector(copySite) || action == @selector(copyUsername) ||
+      action == @selector(copyPassword) ||
+      (_plainTextPasswordShown && action == @selector(hidePassword)) ||
+      (!_plainTextPasswordShown && action == @selector(showPassword))) {
+    return YES;
+  }
+  return NO;
+}
+
 @end
diff --git a/ios/chrome/browser/ui/settings/passwords_settings_egtest.mm b/ios/chrome/browser/ui/settings/passwords_settings_egtest.mm
index ad758cc..00644ad 100644
--- a/ios/chrome/browser/ui/settings/passwords_settings_egtest.mm
+++ b/ios/chrome/browser/ui/settings/passwords_settings_egtest.mm
@@ -175,6 +175,44 @@
       grey_layout(@[ Below() ], PasswordHeader()), nullptr);
 }
 
+// This is similar to grey_ancestor, but only limited to the immediate parent.
+id<GREYMatcher> MatchParentWith(id<GREYMatcher> parentMatcher) {
+  MatchesBlock matches = ^BOOL(id element) {
+    id parent = [element isKindOfClass:[UIView class]]
+                    ? [element superview]
+                    : [element accessibilityContainer];
+    return (parent && [parentMatcher matches:parent]);
+  };
+  DescribeToBlock describe = ^void(id<GREYDescription> description) {
+    [description appendText:[NSString stringWithFormat:@"parentThatMatches(%@)",
+                                                       parentMatcher]];
+  };
+  return grey_allOf(
+      grey_anyOf(grey_kindOfClass([UIView class]),
+                 grey_respondsToSelector(@selector(accessibilityContainer)),
+                 nil),
+      [[GREYElementMatcherBlock alloc] initWithMatchesBlock:matches
+                                           descriptionBlock:describe],
+      nil);
+}
+
+// Matches the pop-up (call-out) menu item with accessibility label equal to the
+// translated string identified by |label|.
+id<GREYMatcher> PopUpMenuItemWithLabel(int label) {
+  // This is a hack relying on UIKit's internal structure. There are multiple
+  // items with the label the test is looking for, because the menu items likely
+  // have the same labels as the buttons for the same function. There is no easy
+  // way to identify elements which are part of the pop-up, because the
+  // associated classes are internal to UIKit. However, the pop-up items are
+  // composed of a button-type element (without accessibility traits of a
+  // button) owning a label, both with the same accessibility labels. This is
+  // differentiating the pop-up items from the other buttons.
+  return grey_allOf(
+      grey_accessibilityLabel(l10n_util::GetNSString(label)),
+      MatchParentWith(grey_accessibilityLabel(l10n_util::GetNSString(label))),
+      nullptr);
+}
+
 }  // namespace
 
 @interface MockReauthenticationModule : NSObject<ReauthenticationProtocol>
@@ -199,6 +237,28 @@
 
 @end
 
+namespace {
+
+// Replace the reauthentication module in
+// PasswordDetailsCollectionViewController with a fake one to avoid being
+// blocked with a reauth prompt, and return the fake reauthentication module.
+MockReauthenticationModule* SetUpAndReturnMockReauthenticationModule() {
+  MockReauthenticationModule* mock_reauthentication_module =
+      [[MockReauthenticationModule alloc] init];
+  SettingsNavigationController* settings_navigation_controller =
+      base::mac::ObjCCastStrict<SettingsNavigationController>(
+          top_view_controller::TopPresentedViewController());
+  PasswordDetailsCollectionViewController*
+      password_details_collection_view_controller =
+          base::mac::ObjCCastStrict<PasswordDetailsCollectionViewController>(
+              settings_navigation_controller.topViewController);
+  [password_details_collection_view_controller
+      setReauthenticationModule:mock_reauthentication_module];
+  return mock_reauthentication_module;
+}
+
+}  // namespace
+
 // Various tests for the Save Passwords section of the settings.
 @interface PasswordsSettingsTestCase : ChromeTestCase
 @end
@@ -279,8 +339,8 @@
 // Saves an example form in the store.
 - (void)saveExamplePasswordForm {
   PasswordForm example;
-  example.username_value = base::ASCIIToUTF16("user");
-  example.password_value = base::ASCIIToUTF16("password");
+  example.username_value = base::ASCIIToUTF16("concrete username");
+  example.password_value = base::ASCIIToUTF16("concrete password");
   example.origin = GURL("https://example.com");
   example.signon_realm = example.origin.spec();
   [self savePasswordFormToStore:example];
@@ -344,7 +404,8 @@
   [self tapDone];
 
   // Inspect "password details" view.
-  [[EarlGrey selectElementWithMatcher:Entry(@"https://example.com, user")]
+  [[EarlGrey
+      selectElementWithMatcher:Entry(@"https://example.com, concrete username")]
       performAction:grey_tap()];
   chrome_test_util::VerifyAccessibilityForCurrentScreen();
   [self tapBackArrow];
@@ -364,23 +425,12 @@
 
   [self openPasswordSettings];
 
-  [[EarlGrey selectElementWithMatcher:Entry(@"https://example.com, user")]
+  [[EarlGrey
+      selectElementWithMatcher:Entry(@"https://example.com, concrete username")]
       performAction:grey_tap()];
 
-  // Get the PasswordDetailsCollectionViewController and replace the
-  // reauthentication module with a fake one to avoid being blocked with a
-  // reauth prompt.
   MockReauthenticationModule* mock_reauthentication_module =
-      [[MockReauthenticationModule alloc] init];
-  SettingsNavigationController* settings_navigation_controller =
-      base::mac::ObjCCastStrict<SettingsNavigationController>(
-          top_view_controller::TopPresentedViewController());
-  PasswordDetailsCollectionViewController*
-      password_details_collection_view_controller =
-          base::mac::ObjCCastStrict<PasswordDetailsCollectionViewController>(
-              settings_navigation_controller.topViewController);
-  [password_details_collection_view_controller
-      setReauthenticationModule:mock_reauthentication_module];
+      SetUpAndReturnMockReauthenticationModule();
 
   // Check the snackbar in case of successful reauthentication.
   mock_reauthentication_module.shouldSucceed = YES;
@@ -427,7 +477,8 @@
 
   [self openPasswordSettings];
 
-  [[EarlGrey selectElementWithMatcher:Entry(@"https://example.com, user")]
+  [[EarlGrey
+      selectElementWithMatcher:Entry(@"https://example.com, concrete username")]
       performAction:grey_tap()];
 
   // Check the snackbar.
@@ -459,7 +510,8 @@
 
   [self openPasswordSettings];
 
-  [[EarlGrey selectElementWithMatcher:Entry(@"https://example.com, user")]
+  [[EarlGrey
+      selectElementWithMatcher:Entry(@"https://example.com, concrete username")]
       performAction:grey_tap()];
 
   // Check the snackbar.
@@ -492,7 +544,8 @@
 
   [self openPasswordSettings];
 
-  [[EarlGrey selectElementWithMatcher:Entry(@"https://example.com, user")]
+  [[EarlGrey
+      selectElementWithMatcher:Entry(@"https://example.com, concrete username")]
       performAction:grey_tap()];
 
   // Tap the Delete... button.
@@ -522,7 +575,8 @@
                             nullptr)] assertWithMatcher:grey_notNil()];
 
   // Also verify that the removed password is no longer in the list.
-  [[EarlGrey selectElementWithMatcher:Entry(@"https://example.com, user")]
+  [[EarlGrey
+      selectElementWithMatcher:Entry(@"https://example.com, concrete username")]
       assertWithMatcher:grey_not(grey_sufficientlyVisible())];
 
   [self tapBackArrow];
@@ -539,7 +593,8 @@
 
   [self openPasswordSettings];
 
-  [[EarlGrey selectElementWithMatcher:Entry(@"https://example.com, user")]
+  [[EarlGrey
+      selectElementWithMatcher:Entry(@"https://example.com, concrete username")]
       performAction:grey_tap()];
 
   // Tap the Delete... button.
@@ -568,7 +623,8 @@
   // Go back to the list view and verify that the password is still in the
   // list.
   [self tapBackArrow];
-  [[EarlGrey selectElementWithMatcher:Entry(@"https://example.com, user")]
+  [[EarlGrey
+      selectElementWithMatcher:Entry(@"https://example.com, concrete username")]
       assertWithMatcher:grey_sufficientlyVisible()];
 
   [self tapBackArrow];
@@ -588,7 +644,8 @@
 
   [self tapEdit];
 
-  [[EarlGrey selectElementWithMatcher:Entry(@"https://example.com, user")]
+  [[EarlGrey
+      selectElementWithMatcher:Entry(@"https://example.com, concrete username")]
       performAction:grey_tap()];
 
   // Check that the current view is not the detail view, by failing to locate
@@ -639,4 +696,203 @@
   [self clearPasswordStore];
 }
 
+// Checks that attempts to copy the site via the context menu item provide an
+// appropriate feedback.
+- (void)testCopySiteMenuItem {
+  [self scopedEnablePasswordManagementAndViewingUI];
+
+  // Saving a form is needed for using the "password details" view.
+  [self saveExamplePasswordForm];
+
+  [self openPasswordSettings];
+
+  [[EarlGrey
+      selectElementWithMatcher:Entry(@"https://example.com, concrete username")]
+      performAction:grey_tap()];
+
+  // Tap the site cell to display the context menu.
+  [[[EarlGrey
+      selectElementWithMatcher:grey_accessibilityLabel(@"https://example.com/")]
+         usingSearchAction:grey_scrollInDirection(kGREYDirectionDown,
+                                                  kScrollAmount)
+      onElementWithMatcher:grey_accessibilityID(
+                               @"PasswordDetailsCollectionViewController")]
+      performAction:grey_tap()];
+
+  // Tap the context menu item for copying.
+  [[EarlGrey selectElementWithMatcher:PopUpMenuItemWithLabel(
+                                          IDS_IOS_SETTINGS_SITE_COPY_MENU_ITEM)]
+      performAction:grey_tap()];
+
+  // Check the snackbar.
+  NSString* snackbarLabel =
+      l10n_util::GetNSString(IDS_IOS_SETTINGS_SITE_WAS_COPIED_MESSAGE);
+  // The tap checks the existence of the snackbar and also closes it.
+  [[EarlGrey selectElementWithMatcher:grey_accessibilityLabel(snackbarLabel)]
+      performAction:grey_tap()];
+
+  [self tapBackArrow];
+  [self tapBackArrow];
+  [self tapDone];
+  [self clearPasswordStore];
+}
+
+// Checks that attempts to copy the username via the context menu item provide
+// an appropriate feedback.
+- (void)testCopyUsernameMenuItem {
+  [self scopedEnablePasswordManagementAndViewingUI];
+
+  // Saving a form is needed for using the "password details" view.
+  [self saveExamplePasswordForm];
+
+  [self openPasswordSettings];
+
+  [[EarlGrey
+      selectElementWithMatcher:Entry(@"https://example.com, concrete username")]
+      performAction:grey_tap()];
+
+  // Tap the username cell to display the context menu.
+  [[[EarlGrey
+      selectElementWithMatcher:grey_accessibilityLabel(@"concrete username")]
+         usingSearchAction:grey_scrollInDirection(kGREYDirectionDown,
+                                                  kScrollAmount)
+      onElementWithMatcher:grey_accessibilityID(
+                               @"PasswordDetailsCollectionViewController")]
+      performAction:grey_tap()];
+
+  // Tap the context menu item for copying.
+  [[EarlGrey
+      selectElementWithMatcher:PopUpMenuItemWithLabel(
+                                   IDS_IOS_SETTINGS_USERNAME_COPY_MENU_ITEM)]
+      performAction:grey_tap()];
+
+  // Check the snackbar.
+  NSString* snackbarLabel =
+      l10n_util::GetNSString(IDS_IOS_SETTINGS_USERNAME_WAS_COPIED_MESSAGE);
+  // The tap checks the existence of the snackbar and also closes it.
+  [[EarlGrey selectElementWithMatcher:grey_accessibilityLabel(snackbarLabel)]
+      performAction:grey_tap()];
+
+  [self tapBackArrow];
+  [self tapBackArrow];
+  [self tapDone];
+  [self clearPasswordStore];
+}
+
+// Checks that attempts to copy the password via the context menu item provide
+// an appropriate feedback.
+- (void)testCopyPasswordMenuItem {
+  [self scopedEnablePasswordManagementAndViewingUI];
+
+  // Saving a form is needed for using the "password details" view.
+  [self saveExamplePasswordForm];
+
+  [self openPasswordSettings];
+
+  [[EarlGrey
+      selectElementWithMatcher:Entry(@"https://example.com, concrete username")]
+      performAction:grey_tap()];
+
+  // Tap the password cell to display the context menu.
+  [[[EarlGrey
+      selectElementWithMatcher:grey_accessibilityLabel(@"●●●●●●●●●●●●●●●●●")]
+         usingSearchAction:grey_scrollInDirection(kGREYDirectionDown,
+                                                  kScrollAmount)
+      onElementWithMatcher:grey_accessibilityID(
+                               @"PasswordDetailsCollectionViewController")]
+      performAction:grey_tap()];
+
+  // Make sure to capture the reauthentication module in a variable until the
+  // end of the test, otherwise it might get deleted too soon and break the
+  // functionality of copying and viewing passwords.
+  MockReauthenticationModule* mock_reauthentication_module =
+      SetUpAndReturnMockReauthenticationModule();
+  mock_reauthentication_module.shouldSucceed = YES;
+
+  // Tap the context menu item for copying.
+  [[EarlGrey
+      selectElementWithMatcher:PopUpMenuItemWithLabel(
+                                   IDS_IOS_SETTINGS_PASSWORD_COPY_MENU_ITEM)]
+      performAction:grey_tap()];
+
+  // Check the snackbar.
+  NSString* snackbarLabel =
+      l10n_util::GetNSString(IDS_IOS_SETTINGS_PASSWORD_WAS_COPIED_MESSAGE);
+  // The tap checks the existence of the snackbar and also closes it.
+  [[EarlGrey selectElementWithMatcher:grey_accessibilityLabel(snackbarLabel)]
+      performAction:grey_tap()];
+
+  [self tapBackArrow];
+  [self tapBackArrow];
+  [self tapDone];
+  [self clearPasswordStore];
+}
+
+// Checks that attempts to show and hide the password via the context menu item
+// provide an appropriate feedback.
+- (void)testShowHidePasswordMenuItem {
+  [self scopedEnablePasswordManagementAndViewingUI];
+
+  // Saving a form is needed for using the "password details" view.
+  [self saveExamplePasswordForm];
+
+  [self openPasswordSettings];
+
+  [[EarlGrey
+      selectElementWithMatcher:Entry(@"https://example.com, concrete username")]
+      performAction:grey_tap()];
+
+  // Tap the password cell to display the context menu.
+  [[[EarlGrey
+      selectElementWithMatcher:grey_accessibilityLabel(@"●●●●●●●●●●●●●●●●●")]
+         usingSearchAction:grey_scrollInDirection(kGREYDirectionDown,
+                                                  kScrollAmount)
+      onElementWithMatcher:grey_accessibilityID(
+                               @"PasswordDetailsCollectionViewController")]
+      performAction:grey_tap()];
+
+  // Make sure to capture the reauthentication module in a variable until the
+  // end of the test, otherwise it might get deleted too soon and break the
+  // functionality of copying and viewing passwords.
+  MockReauthenticationModule* mock_reauthentication_module =
+      SetUpAndReturnMockReauthenticationModule();
+  mock_reauthentication_module.shouldSucceed = YES;
+
+  // Tap the context menu item for showing.
+  [[EarlGrey
+      selectElementWithMatcher:PopUpMenuItemWithLabel(
+                                   IDS_IOS_SETTINGS_PASSWORD_SHOW_MENU_ITEM)]
+      performAction:grey_tap()];
+
+  // Tap the password cell to display the context menu again, and to check that
+  // the password was unmasked.
+  [[[EarlGrey
+      selectElementWithMatcher:grey_accessibilityLabel(@"concrete password")]
+         usingSearchAction:grey_scrollInDirection(kGREYDirectionDown,
+                                                  kScrollAmount)
+      onElementWithMatcher:grey_accessibilityID(
+                               @"PasswordDetailsCollectionViewController")]
+      performAction:grey_tap()];
+
+  // Tap the context menu item for hiding.
+  [[EarlGrey
+      selectElementWithMatcher:PopUpMenuItemWithLabel(
+                                   IDS_IOS_SETTINGS_PASSWORD_HIDE_MENU_ITEM)]
+      performAction:grey_tap()];
+
+  // Check that the password is masked again.
+  [[[EarlGrey
+      selectElementWithMatcher:grey_accessibilityLabel(@"●●●●●●●●●●●●●●●●●")]
+         usingSearchAction:grey_scrollInDirection(kGREYDirectionDown,
+                                                  kScrollAmount)
+      onElementWithMatcher:grey_accessibilityID(
+                               @"PasswordDetailsCollectionViewController")]
+      assertWithMatcher:grey_sufficientlyVisible()];
+
+  [self tapBackArrow];
+  [self tapBackArrow];
+  [self tapDone];
+  [self clearPasswordStore];
+}
+
 @end
diff --git a/ios/chrome/test/app/sync_test_util.mm b/ios/chrome/test/app/sync_test_util.mm
index 0ba439c..bf9a2b2 100644
--- a/ios/chrome/test/app/sync_test_util.mm
+++ b/ios/chrome/test/app/sync_test_util.mm
@@ -23,8 +23,6 @@
 #include "components/sync/test/fake_server/fake_server_network_resources.h"
 #include "components/sync/test/fake_server/fake_server_verifier.h"
 #include "components/sync/test/fake_server/sessions_hierarchy.h"
-#include "components/sync/test/fake_server/tombstone_entity.h"
-#include "components/sync/test/fake_server/unique_client_entity.h"
 #include "ios/chrome/browser/autofill/personal_data_manager_factory.h"
 #include "ios/chrome/browser/browser_state/chrome_browser_state.h"
 #include "ios/chrome/browser/history/history_service_factory.h"
@@ -178,9 +176,9 @@
   autofill_profile->add_name_full(full_name);
   autofill_profile->set_guid(guid);
 
-  std::unique_ptr<fake_server::FakeServerEntity> entity =
-      fake_server::UniqueClientEntity::CreateForInjection(guid,
-                                                          entity_specifics);
+  std::unique_ptr<syncer::LoopbackServerEntity> entity =
+      syncer::PersistentUniqueClientEntity::CreateFromEntitySpecifics(
+          guid, entity_specifics);
   gSyncFakeServer->InjectEntity(std::move(entity));
 }
 
@@ -197,8 +195,9 @@
   }
   // Delete the entity if it exists.
   if (!entity_id.empty()) {
-    std::unique_ptr<fake_server::FakeServerEntity> entity;
-    entity = fake_server::TombstoneEntity::Create(entity_id, std::string());
+    std::unique_ptr<syncer::LoopbackServerEntity> entity;
+    entity =
+        syncer::PersistentTombstoneEntity::CreateNew(entity_id, std::string());
     gSyncFakeServer->InjectEntity(std::move(entity));
   }
 }
@@ -267,8 +266,9 @@
   typedUrl->add_visits(base::Time::Max().ToInternalValue());
   typedUrl->add_visit_transitions(sync_pb::SyncEnums::TYPED);
 
-  std::unique_ptr<fake_server::FakeServerEntity> entity =
-      fake_server::UniqueClientEntity::CreateForInjection(url, entitySpecifics);
+  std::unique_ptr<syncer::LoopbackServerEntity> entity =
+      syncer::PersistentUniqueClientEntity::CreateFromEntitySpecifics(
+          url, entitySpecifics);
   gSyncFakeServer->InjectEntity(std::move(entity));
 }
 
@@ -346,8 +346,9 @@
     }
   }
   if (!entity_id.empty()) {
-    std::unique_ptr<fake_server::FakeServerEntity> entity;
-    entity = fake_server::TombstoneEntity::Create(entity_id, std::string());
+    std::unique_ptr<syncer::LoopbackServerEntity> entity;
+    entity =
+        syncer::PersistentTombstoneEntity::CreateNew(entity_id, std::string());
     gSyncFakeServer->InjectEntity(std::move(entity));
   }
 }
diff --git a/mash/catalog_viewer/catalog_viewer.cc b/mash/catalog_viewer/catalog_viewer.cc
index c3ca399..ccaca82 100644
--- a/mash/catalog_viewer/catalog_viewer.cc
+++ b/mash/catalog_viewer/catalog_viewer.cc
@@ -222,9 +222,11 @@
 }
 
 void CatalogViewer::OnStart() {
-  aura_init_ = base::MakeUnique<views::AuraInit>(
+  aura_init_ = views::AuraInit::Create(
       context()->connector(), context()->identity(), "views_mus_resources.pak",
       std::string(), nullptr, views::AuraInit::Mode::AURA_MUS);
+  if (!aura_init_)
+    context()->QuitNow();
 }
 
 void CatalogViewer::OnBindInterface(
diff --git a/mash/example/views_examples/views_examples.cc b/mash/example/views_examples/views_examples.cc
index e559bf9..da9a8a50 100644
--- a/mash/example/views_examples/views_examples.cc
+++ b/mash/example/views_examples/views_examples.cc
@@ -31,10 +31,12 @@
  private:
   // service_manager::Service:
   void OnStart() override {
-    aura_init_ = base::MakeUnique<views::AuraInit>(
-        context()->connector(), context()->identity(),
-        "views_mus_resources.pak", std::string(), nullptr,
-        views::AuraInit::Mode::AURA_MUS);
+    aura_init_ =
+        views::AuraInit::Create(context()->connector(), context()->identity(),
+                                "views_mus_resources.pak", std::string(),
+                                nullptr, views::AuraInit::Mode::AURA_MUS);
+    if (!aura_init_)
+      context()->QuitNow();
   }
   void OnBindInterface(const service_manager::BindSourceInfo& source_info,
                        const std::string& interface_name,
diff --git a/mash/example/window_type_launcher/window_type_launcher.cc b/mash/example/window_type_launcher/window_type_launcher.cc
index 021135e4..078005b9 100644
--- a/mash/example/window_type_launcher/window_type_launcher.cc
+++ b/mash/example/window_type_launcher/window_type_launcher.cc
@@ -453,9 +453,11 @@
 }
 
 void WindowTypeLauncher::OnStart() {
-  aura_init_ = base::MakeUnique<views::AuraInit>(
+  aura_init_ = views::AuraInit::Create(
       context()->connector(), context()->identity(), "views_mus_resources.pak",
       std::string(), nullptr, views::AuraInit::Mode::AURA_MUS);
+  if (!aura_init_)
+    context()->QuitNow();
 }
 
 void WindowTypeLauncher::OnBindInterface(
diff --git a/mash/quick_launch/quick_launch.cc b/mash/quick_launch/quick_launch.cc
index a9f51a0f..1a5dafa 100644
--- a/mash/quick_launch/quick_launch.cc
+++ b/mash/quick_launch/quick_launch.cc
@@ -171,14 +171,13 @@
 }
 
 void QuickLaunch::OnStart() {
-  aura_init_ = base::MakeUnique<views::AuraInit>(
-      context()->connector(), context()->identity(), "views_mus_resources.pak",
-      std::string(), nullptr, views::AuraInit::Mode::AURA_MUS);
-
   // If AuraInit was unable to initialize there is no longer a peer connection.
   // The ServiceManager is in the process of shutting down, however we haven't
   // been notified yet. Close our ServiceContext and shutdown.
-  if (!aura_init_->initialized()) {
+  aura_init_ = views::AuraInit::Create(
+      context()->connector(), context()->identity(), "views_mus_resources.pak",
+      std::string(), nullptr, views::AuraInit::Mode::AURA_MUS);
+  if (!aura_init_) {
     context()->QuitNow();
     return;
   }
diff --git a/mash/simple_wm/simple_wm.cc b/mash/simple_wm/simple_wm.cc
index 8850a38..26ba8465 100644
--- a/mash/simple_wm/simple_wm.cc
+++ b/mash/simple_wm/simple_wm.cc
@@ -364,9 +364,13 @@
   started_ = true;
   screen_ = base::MakeUnique<display::ScreenBase>();
   display::Screen::SetScreenInstance(screen_.get());
-  aura_init_ = base::MakeUnique<views::AuraInit>(
+  aura_init_ = views::AuraInit::Create(
       context()->connector(), context()->identity(), "views_mus_resources.pak",
       std::string(), nullptr, views::AuraInit::Mode::AURA_MUS_WINDOW_MANAGER);
+  if (!aura_init_) {
+    context()->QuitNow();
+    return;
+  }
   window_tree_client_ = base::MakeUnique<aura::WindowTreeClient>(
       context()->connector(), this, this);
   aura::Env::GetInstance()->SetWindowTreeClient(window_tree_client_.get());
diff --git a/mash/task_viewer/task_viewer.cc b/mash/task_viewer/task_viewer.cc
index cdc03e76..d932576 100644
--- a/mash/task_viewer/task_viewer.cc
+++ b/mash/task_viewer/task_viewer.cc
@@ -294,9 +294,11 @@
 }
 
 void TaskViewer::OnStart() {
-  aura_init_ = base::MakeUnique<views::AuraInit>(
+  aura_init_ = views::AuraInit::Create(
       context()->connector(), context()->identity(), "views_mus_resources.pak",
       std::string(), nullptr, views::AuraInit::Mode::AURA_MUS);
+  if (!aura_init_)
+    context()->QuitNow();
 }
 
 void TaskViewer::OnBindInterface(
diff --git a/remoting/ios/app/BUILD.gn b/remoting/ios/app/BUILD.gn
index 542aed00..6b442a6e 100644
--- a/remoting/ios/app/BUILD.gn
+++ b/remoting/ios/app/BUILD.gn
@@ -96,6 +96,5 @@
     ":main",
     "//remoting/ios/app/resources:launchscreen_assets",
     "//remoting/ios/app/resources:remoting_icons",
-    "//remoting/ios/app/resources:system_icons",
   ]
 }
diff --git a/remoting/ios/app/remoting_theme.mm b/remoting/ios/app/remoting_theme.mm
index 0430e5e..1b4a86c 100644
--- a/remoting/ios/app/remoting_theme.mm
+++ b/remoting/ios/app/remoting_theme.mm
@@ -114,7 +114,7 @@
   static UIImage* icon;
   static dispatch_once_t onceToken;
   dispatch_once(&onceToken, ^{
-    icon = [UIImage imageNamed:@"Back"];
+    icon = [UIImage imageNamed:@"ic_chevron_left_white_36pt"];
   });
   return icon;
 }
@@ -123,7 +123,7 @@
   static UIImage* icon;
   static dispatch_once_t onceToken;
   dispatch_once(&onceToken, ^{
-    icon = [UIImage imageNamed:@"ic_check_box"];
+    icon = [UIImage imageNamed:@"ic_check_box_white"];
   });
   return icon;
 }
@@ -132,7 +132,7 @@
   static UIImage* icon;
   static dispatch_once_t onceToken;
   dispatch_once(&onceToken, ^{
-    icon = [UIImage imageNamed:@"ic_check_box_outline_blank"];
+    icon = [UIImage imageNamed:@"ic_check_box_outline_blank_white"];
   });
   return icon;
 }
@@ -141,7 +141,7 @@
   static UIImage* icon;
   static dispatch_once_t onceToken;
   dispatch_once(&onceToken, ^{
-    icon = [UIImage imageNamed:@"ic_close"];
+    icon = [UIImage imageNamed:@"ic_close_white"];
   });
   return icon;
 }
@@ -150,7 +150,7 @@
   static UIImage* icon;
   static dispatch_once_t onceToken;
   dispatch_once(&onceToken, ^{
-    icon = [UIImage imageNamed:@"ic_desktop"];
+    icon = [UIImage imageNamed:@"ic_desktop_windows_white"];
   });
   return icon;
 }
@@ -159,7 +159,7 @@
   static UIImage* icon;
   static dispatch_once_t onceToken;
   dispatch_once(&onceToken, ^{
-    icon = [UIImage imageNamed:@"ic_menu"];
+    icon = [UIImage imageNamed:@"ic_menu_white"];
   });
   return icon;
 }
@@ -168,7 +168,7 @@
   static UIImage* icon;
   static dispatch_once_t onceToken;
   dispatch_once(&onceToken, ^{
-    icon = [UIImage imageNamed:@"ic_radio_button_checked"];
+    icon = [UIImage imageNamed:@"ic_radio_button_checked_white"];
   });
   return icon;
 }
@@ -177,7 +177,7 @@
   static UIImage* icon;
   static dispatch_once_t onceToken;
   dispatch_once(&onceToken, ^{
-    icon = [UIImage imageNamed:@"ic_radio_button_unchecked"];
+    icon = [UIImage imageNamed:@"ic_radio_button_unchecked_white"];
   });
   return icon;
 }
@@ -186,7 +186,7 @@
   static UIImage* icon;
   static dispatch_once_t onceToken;
   dispatch_once(&onceToken, ^{
-    icon = [UIImage imageNamed:@"ic_refresh"];
+    icon = [UIImage imageNamed:@"ic_refresh_white"];
   });
   return icon;
 }
@@ -195,7 +195,7 @@
   static UIImage* icon;
   static dispatch_once_t onceToken;
   dispatch_once(&onceToken, ^{
-    icon = [UIImage imageNamed:@"Settings"];
+    icon = [UIImage imageNamed:@"ic_settings_white"];
   });
   return icon;
 }
diff --git a/remoting/ios/app/resources/Assets.xcassets/Back.imageset/Contents.json b/remoting/ios/app/resources/Assets.xcassets/Back.imageset/Contents.json
deleted file mode 100644
index 76e6b85..0000000
--- a/remoting/ios/app/resources/Assets.xcassets/Back.imageset/Contents.json
+++ /dev/null
@@ -1,23 +0,0 @@
-{
-  "images" : [
-    {
-      "idiom" : "universal",
-      "filename" : "ic_keyboard_arrow_left_white_1x_ios_36dp.png",
-      "scale" : "1x"
-    },
-    {
-      "idiom" : "universal",
-      "filename" : "ic_keyboard_arrow_left_white_2x_ios_36dp.png",
-      "scale" : "2x"
-    },
-    {
-      "idiom" : "universal",
-      "filename" : "ic_keyboard_arrow_left_white_3x_ios_36dp.png",
-      "scale" : "3x"
-    }
-  ],
-  "info" : {
-    "version" : 1,
-    "author" : "xcode"
-  }
-}
\ No newline at end of file
diff --git a/remoting/ios/app/resources/Assets.xcassets/Back.imageset/ic_keyboard_arrow_left_white_1x_ios_36dp.png b/remoting/ios/app/resources/Assets.xcassets/Back.imageset/ic_keyboard_arrow_left_white_1x_ios_36dp.png
deleted file mode 100644
index e9a1ed9..0000000
--- a/remoting/ios/app/resources/Assets.xcassets/Back.imageset/ic_keyboard_arrow_left_white_1x_ios_36dp.png
+++ /dev/null
Binary files differ
diff --git a/remoting/ios/app/resources/Assets.xcassets/Back.imageset/ic_keyboard_arrow_left_white_2x_ios_36dp.png b/remoting/ios/app/resources/Assets.xcassets/Back.imageset/ic_keyboard_arrow_left_white_2x_ios_36dp.png
deleted file mode 100644
index d8af01c8..0000000
--- a/remoting/ios/app/resources/Assets.xcassets/Back.imageset/ic_keyboard_arrow_left_white_2x_ios_36dp.png
+++ /dev/null
Binary files differ
diff --git a/remoting/ios/app/resources/Assets.xcassets/Back.imageset/ic_keyboard_arrow_left_white_3x_ios_36dp.png b/remoting/ios/app/resources/Assets.xcassets/Back.imageset/ic_keyboard_arrow_left_white_3x_ios_36dp.png
deleted file mode 100644
index 3b1cf4ae..0000000
--- a/remoting/ios/app/resources/Assets.xcassets/Back.imageset/ic_keyboard_arrow_left_white_3x_ios_36dp.png
+++ /dev/null
Binary files differ
diff --git a/remoting/ios/app/resources/Assets.xcassets/Settings.imageset/Contents.json b/remoting/ios/app/resources/Assets.xcassets/Settings.imageset/Contents.json
deleted file mode 100644
index 6b283d8..0000000
--- a/remoting/ios/app/resources/Assets.xcassets/Settings.imageset/Contents.json
+++ /dev/null
@@ -1,23 +0,0 @@
-{
-  "images" : [
-    {
-      "idiom" : "universal",
-      "filename" : "ic_settings_white_1x_ios_24dp.png",
-      "scale" : "1x"
-    },
-    {
-      "idiom" : "universal",
-      "filename" : "ic_settings_white_2x_ios_24dp.png",
-      "scale" : "2x"
-    },
-    {
-      "idiom" : "universal",
-      "filename" : "ic_settings_white_3x_ios_24dp.png",
-      "scale" : "3x"
-    }
-  ],
-  "info" : {
-    "version" : 1,
-    "author" : "xcode"
-  }
-}
\ No newline at end of file
diff --git a/remoting/ios/app/resources/Assets.xcassets/Settings.imageset/ic_settings_white_1x_ios_24dp.png b/remoting/ios/app/resources/Assets.xcassets/Settings.imageset/ic_settings_white_1x_ios_24dp.png
deleted file mode 100644
index 8909c35..0000000
--- a/remoting/ios/app/resources/Assets.xcassets/Settings.imageset/ic_settings_white_1x_ios_24dp.png
+++ /dev/null
Binary files differ
diff --git a/remoting/ios/app/resources/Assets.xcassets/Settings.imageset/ic_settings_white_2x_ios_24dp.png b/remoting/ios/app/resources/Assets.xcassets/Settings.imageset/ic_settings_white_2x_ios_24dp.png
deleted file mode 100644
index 5caedc8e..0000000
--- a/remoting/ios/app/resources/Assets.xcassets/Settings.imageset/ic_settings_white_2x_ios_24dp.png
+++ /dev/null
Binary files differ
diff --git a/remoting/ios/app/resources/Assets.xcassets/Settings.imageset/ic_settings_white_3x_ios_24dp.png b/remoting/ios/app/resources/Assets.xcassets/Settings.imageset/ic_settings_white_3x_ios_24dp.png
deleted file mode 100644
index eabb0a2b..0000000
--- a/remoting/ios/app/resources/Assets.xcassets/Settings.imageset/ic_settings_white_3x_ios_24dp.png
+++ /dev/null
Binary files differ
diff --git a/remoting/ios/app/resources/Assets.xcassets/ic_arrow_forward_white.imageset/Contents.json b/remoting/ios/app/resources/Assets.xcassets/ic_arrow_forward_white.imageset/Contents.json
deleted file mode 100644
index 5dc3dc40..0000000
--- a/remoting/ios/app/resources/Assets.xcassets/ic_arrow_forward_white.imageset/Contents.json
+++ /dev/null
@@ -1,23 +0,0 @@
-{
-    "images": [
-        {
-            "filename": "ic_arrow_forward_white.png",
-            "idiom": "universal",
-            "scale": "1x"
-        },
-        {
-            "filename": "ic_arrow_forward_white_2x.png",
-            "idiom": "universal",
-            "scale": "2x"
-        },
-        {
-            "filename": "ic_arrow_forward_white_3x.png",
-            "idiom": "universal",
-            "scale": "3x"
-        }
-    ],
-    "info": {
-        "author": "xcode",
-        "version": 1
-    }
-}
\ No newline at end of file
diff --git a/remoting/ios/app/resources/Assets.xcassets/ic_arrow_forward_white.imageset/ic_arrow_forward_white.png b/remoting/ios/app/resources/Assets.xcassets/ic_arrow_forward_white.imageset/ic_arrow_forward_white.png
deleted file mode 100644
index 552d40ded..0000000
--- a/remoting/ios/app/resources/Assets.xcassets/ic_arrow_forward_white.imageset/ic_arrow_forward_white.png
+++ /dev/null
Binary files differ
diff --git a/remoting/ios/app/resources/Assets.xcassets/ic_arrow_forward_white.imageset/ic_arrow_forward_white_2x.png b/remoting/ios/app/resources/Assets.xcassets/ic_arrow_forward_white.imageset/ic_arrow_forward_white_2x.png
deleted file mode 100644
index 878b6e5..0000000
--- a/remoting/ios/app/resources/Assets.xcassets/ic_arrow_forward_white.imageset/ic_arrow_forward_white_2x.png
+++ /dev/null
Binary files differ
diff --git a/remoting/ios/app/resources/Assets.xcassets/ic_arrow_forward_white.imageset/ic_arrow_forward_white_3x.png b/remoting/ios/app/resources/Assets.xcassets/ic_arrow_forward_white.imageset/ic_arrow_forward_white_3x.png
deleted file mode 100644
index f9ff42f..0000000
--- a/remoting/ios/app/resources/Assets.xcassets/ic_arrow_forward_white.imageset/ic_arrow_forward_white_3x.png
+++ /dev/null
Binary files differ
diff --git a/remoting/ios/app/resources/Assets.xcassets/ic_check_box.imageset/Contents.json b/remoting/ios/app/resources/Assets.xcassets/ic_check_box.imageset/Contents.json
deleted file mode 100644
index ff99ef1..0000000
--- a/remoting/ios/app/resources/Assets.xcassets/ic_check_box.imageset/Contents.json
+++ /dev/null
@@ -1,23 +0,0 @@
-{
-  "images" : [
-    {
-      "idiom" : "universal",
-      "filename" : "ic_check_box_white_1x_ios_24dp.png",
-      "scale" : "1x"
-    },
-    {
-      "idiom" : "universal",
-      "filename" : "ic_check_box_white_2x_ios_24dp.png",
-      "scale" : "2x"
-    },
-    {
-      "idiom" : "universal",
-      "filename" : "ic_check_box_white_3x_ios_24dp.png",
-      "scale" : "3x"
-    }
-  ],
-  "info" : {
-    "version" : 1,
-    "author" : "xcode"
-  }
-}
\ No newline at end of file
diff --git a/remoting/ios/app/resources/Assets.xcassets/ic_check_box.imageset/ic_check_box_white_1x_ios_24dp.png b/remoting/ios/app/resources/Assets.xcassets/ic_check_box.imageset/ic_check_box_white_1x_ios_24dp.png
deleted file mode 100644
index ff08f82f..0000000
--- a/remoting/ios/app/resources/Assets.xcassets/ic_check_box.imageset/ic_check_box_white_1x_ios_24dp.png
+++ /dev/null
Binary files differ
diff --git a/remoting/ios/app/resources/Assets.xcassets/ic_check_box.imageset/ic_check_box_white_2x_ios_24dp.png b/remoting/ios/app/resources/Assets.xcassets/ic_check_box.imageset/ic_check_box_white_2x_ios_24dp.png
deleted file mode 100644
index 82279a2..0000000
--- a/remoting/ios/app/resources/Assets.xcassets/ic_check_box.imageset/ic_check_box_white_2x_ios_24dp.png
+++ /dev/null
Binary files differ
diff --git a/remoting/ios/app/resources/Assets.xcassets/ic_check_box.imageset/ic_check_box_white_3x_ios_24dp.png b/remoting/ios/app/resources/Assets.xcassets/ic_check_box.imageset/ic_check_box_white_3x_ios_24dp.png
deleted file mode 100644
index a5a1d75a..0000000
--- a/remoting/ios/app/resources/Assets.xcassets/ic_check_box.imageset/ic_check_box_white_3x_ios_24dp.png
+++ /dev/null
Binary files differ
diff --git a/remoting/ios/app/resources/Assets.xcassets/ic_check_box_outline_blank.imageset/Contents.json b/remoting/ios/app/resources/Assets.xcassets/ic_check_box_outline_blank.imageset/Contents.json
deleted file mode 100644
index 131409bf..0000000
--- a/remoting/ios/app/resources/Assets.xcassets/ic_check_box_outline_blank.imageset/Contents.json
+++ /dev/null
@@ -1,23 +0,0 @@
-{
-  "images" : [
-    {
-      "idiom" : "universal",
-      "filename" : "ic_check_box_outline_blank_white_1x_ios_24dp.png",
-      "scale" : "1x"
-    },
-    {
-      "idiom" : "universal",
-      "filename" : "ic_check_box_outline_blank_white_2x_ios_24dp.png",
-      "scale" : "2x"
-    },
-    {
-      "idiom" : "universal",
-      "filename" : "ic_check_box_outline_blank_white_3x_ios_24dp.png",
-      "scale" : "3x"
-    }
-  ],
-  "info" : {
-    "version" : 1,
-    "author" : "xcode"
-  }
-}
\ No newline at end of file
diff --git a/remoting/ios/app/resources/Assets.xcassets/ic_check_box_outline_blank.imageset/ic_check_box_outline_blank_white_1x_ios_24dp.png b/remoting/ios/app/resources/Assets.xcassets/ic_check_box_outline_blank.imageset/ic_check_box_outline_blank_white_1x_ios_24dp.png
deleted file mode 100644
index 9951dfe..0000000
--- a/remoting/ios/app/resources/Assets.xcassets/ic_check_box_outline_blank.imageset/ic_check_box_outline_blank_white_1x_ios_24dp.png
+++ /dev/null
Binary files differ
diff --git a/remoting/ios/app/resources/Assets.xcassets/ic_check_box_outline_blank.imageset/ic_check_box_outline_blank_white_2x_ios_24dp.png b/remoting/ios/app/resources/Assets.xcassets/ic_check_box_outline_blank.imageset/ic_check_box_outline_blank_white_2x_ios_24dp.png
deleted file mode 100644
index 71c52ffc..0000000
--- a/remoting/ios/app/resources/Assets.xcassets/ic_check_box_outline_blank.imageset/ic_check_box_outline_blank_white_2x_ios_24dp.png
+++ /dev/null
Binary files differ
diff --git a/remoting/ios/app/resources/Assets.xcassets/ic_check_box_outline_blank.imageset/ic_check_box_outline_blank_white_3x_ios_24dp.png b/remoting/ios/app/resources/Assets.xcassets/ic_check_box_outline_blank.imageset/ic_check_box_outline_blank_white_3x_ios_24dp.png
deleted file mode 100644
index 64a8105a..0000000
--- a/remoting/ios/app/resources/Assets.xcassets/ic_check_box_outline_blank.imageset/ic_check_box_outline_blank_white_3x_ios_24dp.png
+++ /dev/null
Binary files differ
diff --git a/remoting/ios/app/resources/Assets.xcassets/ic_desktop.imageset/Contents.json b/remoting/ios/app/resources/Assets.xcassets/ic_desktop.imageset/Contents.json
deleted file mode 100644
index 78dd22b11..0000000
--- a/remoting/ios/app/resources/Assets.xcassets/ic_desktop.imageset/Contents.json
+++ /dev/null
@@ -1,23 +0,0 @@
-{
-  "images" : [
-    {
-      "idiom" : "universal",
-      "filename" : "ic_desktop.png",
-      "scale" : "1x"
-    },
-    {
-      "idiom" : "universal",
-      "filename" : "ic_desktop@2x.png",
-      "scale" : "2x"
-    },
-    {
-      "idiom" : "universal",
-      "filename" : "ic_desktop@3x.png",
-      "scale" : "3x"
-    }
-  ],
-  "info" : {
-    "version" : 1,
-    "author" : "xcode"
-  }
-}
\ No newline at end of file
diff --git a/remoting/ios/app/resources/Assets.xcassets/ic_desktop.imageset/ic_desktop.png b/remoting/ios/app/resources/Assets.xcassets/ic_desktop.imageset/ic_desktop.png
deleted file mode 100644
index bf893aad..0000000
--- a/remoting/ios/app/resources/Assets.xcassets/ic_desktop.imageset/ic_desktop.png
+++ /dev/null
Binary files differ
diff --git a/remoting/ios/app/resources/Assets.xcassets/ic_desktop.imageset/ic_desktop@2x.png b/remoting/ios/app/resources/Assets.xcassets/ic_desktop.imageset/ic_desktop@2x.png
deleted file mode 100644
index bd811e6..0000000
--- a/remoting/ios/app/resources/Assets.xcassets/ic_desktop.imageset/ic_desktop@2x.png
+++ /dev/null
Binary files differ
diff --git a/remoting/ios/app/resources/Assets.xcassets/ic_desktop.imageset/ic_desktop@3x.png b/remoting/ios/app/resources/Assets.xcassets/ic_desktop.imageset/ic_desktop@3x.png
deleted file mode 100644
index fbcec23..0000000
--- a/remoting/ios/app/resources/Assets.xcassets/ic_desktop.imageset/ic_desktop@3x.png
+++ /dev/null
Binary files differ
diff --git a/remoting/ios/app/resources/Assets.xcassets/ic_desktop_windows.imageset/Contents.json b/remoting/ios/app/resources/Assets.xcassets/ic_desktop_windows.imageset/Contents.json
deleted file mode 100644
index e9c4c8b..0000000
--- a/remoting/ios/app/resources/Assets.xcassets/ic_desktop_windows.imageset/Contents.json
+++ /dev/null
@@ -1,23 +0,0 @@
-{
-  "images" : [
-    {
-      "idiom" : "universal",
-      "filename" : "ic_desktop_windows.png",
-      "scale" : "1x"
-    },
-    {
-      "idiom" : "universal",
-      "filename" : "ic_desktop_windows@2x.png",
-      "scale" : "2x"
-    },
-    {
-      "idiom" : "universal",
-      "filename" : "ic_desktop_windows@3x.png",
-      "scale" : "3x"
-    }
-  ],
-  "info" : {
-    "version" : 1,
-    "author" : "xcode"
-  }
-}
\ No newline at end of file
diff --git a/remoting/ios/app/resources/Assets.xcassets/ic_desktop_windows.imageset/ic_desktop_windows.png b/remoting/ios/app/resources/Assets.xcassets/ic_desktop_windows.imageset/ic_desktop_windows.png
deleted file mode 100644
index 72a21bb..0000000
--- a/remoting/ios/app/resources/Assets.xcassets/ic_desktop_windows.imageset/ic_desktop_windows.png
+++ /dev/null
Binary files differ
diff --git a/remoting/ios/app/resources/Assets.xcassets/ic_desktop_windows.imageset/ic_desktop_windows@2x.png b/remoting/ios/app/resources/Assets.xcassets/ic_desktop_windows.imageset/ic_desktop_windows@2x.png
deleted file mode 100644
index a6cd393..0000000
--- a/remoting/ios/app/resources/Assets.xcassets/ic_desktop_windows.imageset/ic_desktop_windows@2x.png
+++ /dev/null
Binary files differ
diff --git a/remoting/ios/app/resources/Assets.xcassets/ic_desktop_windows.imageset/ic_desktop_windows@3x.png b/remoting/ios/app/resources/Assets.xcassets/ic_desktop_windows.imageset/ic_desktop_windows@3x.png
deleted file mode 100644
index 0567bf3..0000000
--- a/remoting/ios/app/resources/Assets.xcassets/ic_desktop_windows.imageset/ic_desktop_windows@3x.png
+++ /dev/null
Binary files differ
diff --git a/remoting/ios/app/resources/Assets.xcassets/ic_fullscreen.imageset/Contents.json b/remoting/ios/app/resources/Assets.xcassets/ic_fullscreen.imageset/Contents.json
deleted file mode 100644
index 4a68ae8..0000000
--- a/remoting/ios/app/resources/Assets.xcassets/ic_fullscreen.imageset/Contents.json
+++ /dev/null
@@ -1,23 +0,0 @@
-{
-  "images" : [
-    {
-      "idiom" : "universal",
-      "filename" : "ic_fullscreen.png",
-      "scale" : "1x"
-    },
-    {
-      "idiom" : "universal",
-      "filename" : "ic_fullscreen@2x.png",
-      "scale" : "2x"
-    },
-    {
-      "idiom" : "universal",
-      "filename" : "ic_fullscreen@3x.png",
-      "scale" : "3x"
-    }
-  ],
-  "info" : {
-    "version" : 1,
-    "author" : "xcode"
-  }
-}
\ No newline at end of file
diff --git a/remoting/ios/app/resources/Assets.xcassets/ic_fullscreen.imageset/ic_fullscreen.png b/remoting/ios/app/resources/Assets.xcassets/ic_fullscreen.imageset/ic_fullscreen.png
deleted file mode 100644
index 3553d6a..0000000
--- a/remoting/ios/app/resources/Assets.xcassets/ic_fullscreen.imageset/ic_fullscreen.png
+++ /dev/null
Binary files differ
diff --git a/remoting/ios/app/resources/Assets.xcassets/ic_fullscreen.imageset/ic_fullscreen@2x.png b/remoting/ios/app/resources/Assets.xcassets/ic_fullscreen.imageset/ic_fullscreen@2x.png
deleted file mode 100644
index 917e418a..0000000
--- a/remoting/ios/app/resources/Assets.xcassets/ic_fullscreen.imageset/ic_fullscreen@2x.png
+++ /dev/null
Binary files differ
diff --git a/remoting/ios/app/resources/Assets.xcassets/ic_fullscreen.imageset/ic_fullscreen@3x.png b/remoting/ios/app/resources/Assets.xcassets/ic_fullscreen.imageset/ic_fullscreen@3x.png
deleted file mode 100644
index 66a373c..0000000
--- a/remoting/ios/app/resources/Assets.xcassets/ic_fullscreen.imageset/ic_fullscreen@3x.png
+++ /dev/null
Binary files differ
diff --git a/remoting/ios/app/resources/Assets.xcassets/ic_fullscreen_exit.imageset/Contents.json b/remoting/ios/app/resources/Assets.xcassets/ic_fullscreen_exit.imageset/Contents.json
deleted file mode 100644
index 93f5d79..0000000
--- a/remoting/ios/app/resources/Assets.xcassets/ic_fullscreen_exit.imageset/Contents.json
+++ /dev/null
@@ -1,23 +0,0 @@
-{
-  "images" : [
-    {
-      "idiom" : "universal",
-      "filename" : "ic_fullscreen_exit.png",
-      "scale" : "1x"
-    },
-    {
-      "idiom" : "universal",
-      "filename" : "ic_fullscreen_exit@2x.png",
-      "scale" : "2x"
-    },
-    {
-      "idiom" : "universal",
-      "filename" : "ic_fullscreen_exit@3x.png",
-      "scale" : "3x"
-    }
-  ],
-  "info" : {
-    "version" : 1,
-    "author" : "xcode"
-  }
-}
\ No newline at end of file
diff --git a/remoting/ios/app/resources/Assets.xcassets/ic_fullscreen_exit.imageset/ic_fullscreen_exit.png b/remoting/ios/app/resources/Assets.xcassets/ic_fullscreen_exit.imageset/ic_fullscreen_exit.png
deleted file mode 100644
index c839448..0000000
--- a/remoting/ios/app/resources/Assets.xcassets/ic_fullscreen_exit.imageset/ic_fullscreen_exit.png
+++ /dev/null
Binary files differ
diff --git a/remoting/ios/app/resources/Assets.xcassets/ic_fullscreen_exit.imageset/ic_fullscreen_exit@2x.png b/remoting/ios/app/resources/Assets.xcassets/ic_fullscreen_exit.imageset/ic_fullscreen_exit@2x.png
deleted file mode 100644
index 5fc3166a..0000000
--- a/remoting/ios/app/resources/Assets.xcassets/ic_fullscreen_exit.imageset/ic_fullscreen_exit@2x.png
+++ /dev/null
Binary files differ
diff --git a/remoting/ios/app/resources/Assets.xcassets/ic_fullscreen_exit.imageset/ic_fullscreen_exit@3x.png b/remoting/ios/app/resources/Assets.xcassets/ic_fullscreen_exit.imageset/ic_fullscreen_exit@3x.png
deleted file mode 100644
index 5691b55..0000000
--- a/remoting/ios/app/resources/Assets.xcassets/ic_fullscreen_exit.imageset/ic_fullscreen_exit@3x.png
+++ /dev/null
Binary files differ
diff --git a/remoting/ios/app/resources/Assets.xcassets/ic_menu.imageset/Contents.json b/remoting/ios/app/resources/Assets.xcassets/ic_menu.imageset/Contents.json
deleted file mode 100644
index 29b2ae0..0000000
--- a/remoting/ios/app/resources/Assets.xcassets/ic_menu.imageset/Contents.json
+++ /dev/null
@@ -1,23 +0,0 @@
-{
-  "images" : [
-    {
-      "idiom" : "universal",
-      "filename" : "ic_menu_white.png",
-      "scale" : "1x"
-    },
-    {
-      "idiom" : "universal",
-      "filename" : "ic_menu_white_2x.png",
-      "scale" : "2x"
-    },
-    {
-      "idiom" : "universal",
-      "filename" : "ic_menu_white_3x.png",
-      "scale" : "3x"
-    }
-  ],
-  "info" : {
-    "version" : 1,
-    "author" : "xcode"
-  }
-}
\ No newline at end of file
diff --git a/remoting/ios/app/resources/Assets.xcassets/ic_menu.imageset/ic_menu_white.png b/remoting/ios/app/resources/Assets.xcassets/ic_menu.imageset/ic_menu_white.png
deleted file mode 100644
index d3cec056..0000000
--- a/remoting/ios/app/resources/Assets.xcassets/ic_menu.imageset/ic_menu_white.png
+++ /dev/null
Binary files differ
diff --git a/remoting/ios/app/resources/Assets.xcassets/ic_menu.imageset/ic_menu_white_2x.png b/remoting/ios/app/resources/Assets.xcassets/ic_menu.imageset/ic_menu_white_2x.png
deleted file mode 100644
index 193185fd..0000000
--- a/remoting/ios/app/resources/Assets.xcassets/ic_menu.imageset/ic_menu_white_2x.png
+++ /dev/null
Binary files differ
diff --git a/remoting/ios/app/resources/Assets.xcassets/ic_menu.imageset/ic_menu_white_3x.png b/remoting/ios/app/resources/Assets.xcassets/ic_menu.imageset/ic_menu_white_3x.png
deleted file mode 100644
index 9cb03482..0000000
--- a/remoting/ios/app/resources/Assets.xcassets/ic_menu.imageset/ic_menu_white_3x.png
+++ /dev/null
Binary files differ
diff --git a/remoting/ios/app/resources/Assets.xcassets/ic_mouse.imageset/Contents.json b/remoting/ios/app/resources/Assets.xcassets/ic_mouse.imageset/Contents.json
deleted file mode 100644
index 5596401b..0000000
--- a/remoting/ios/app/resources/Assets.xcassets/ic_mouse.imageset/Contents.json
+++ /dev/null
@@ -1,23 +0,0 @@
-{
-  "images" : [
-    {
-      "idiom" : "universal",
-      "filename" : "ic_mouse.png",
-      "scale" : "1x"
-    },
-    {
-      "idiom" : "universal",
-      "filename" : "ic_mouse@2x.png",
-      "scale" : "2x"
-    },
-    {
-      "idiom" : "universal",
-      "filename" : "ic_mouse@3x.png",
-      "scale" : "3x"
-    }
-  ],
-  "info" : {
-    "version" : 1,
-    "author" : "xcode"
-  }
-}
\ No newline at end of file
diff --git a/remoting/ios/app/resources/Assets.xcassets/ic_mouse.imageset/ic_mouse.png b/remoting/ios/app/resources/Assets.xcassets/ic_mouse.imageset/ic_mouse.png
deleted file mode 100644
index 5e06433f..0000000
--- a/remoting/ios/app/resources/Assets.xcassets/ic_mouse.imageset/ic_mouse.png
+++ /dev/null
Binary files differ
diff --git a/remoting/ios/app/resources/Assets.xcassets/ic_mouse.imageset/ic_mouse@2x.png b/remoting/ios/app/resources/Assets.xcassets/ic_mouse.imageset/ic_mouse@2x.png
deleted file mode 100644
index aff1d9c..0000000
--- a/remoting/ios/app/resources/Assets.xcassets/ic_mouse.imageset/ic_mouse@2x.png
+++ /dev/null
Binary files differ
diff --git a/remoting/ios/app/resources/Assets.xcassets/ic_mouse.imageset/ic_mouse@3x.png b/remoting/ios/app/resources/Assets.xcassets/ic_mouse.imageset/ic_mouse@3x.png
deleted file mode 100644
index deac754..0000000
--- a/remoting/ios/app/resources/Assets.xcassets/ic_mouse.imageset/ic_mouse@3x.png
+++ /dev/null
Binary files differ
diff --git a/remoting/ios/app/resources/Assets.xcassets/ic_radio_button_checked.imageset/Contents.json b/remoting/ios/app/resources/Assets.xcassets/ic_radio_button_checked.imageset/Contents.json
deleted file mode 100644
index ba265ca1..0000000
--- a/remoting/ios/app/resources/Assets.xcassets/ic_radio_button_checked.imageset/Contents.json
+++ /dev/null
@@ -1,23 +0,0 @@
-{
-  "images" : [
-    {
-      "idiom" : "universal",
-      "filename" : "ic_radio_button_checked_white_1x_ios_24dp.png",
-      "scale" : "1x"
-    },
-    {
-      "idiom" : "universal",
-      "filename" : "ic_radio_button_checked_white_2x_ios_24dp.png",
-      "scale" : "2x"
-    },
-    {
-      "idiom" : "universal",
-      "filename" : "ic_radio_button_checked_white_3x_ios_24dp.png",
-      "scale" : "3x"
-    }
-  ],
-  "info" : {
-    "version" : 1,
-    "author" : "xcode"
-  }
-}
\ No newline at end of file
diff --git a/remoting/ios/app/resources/Assets.xcassets/ic_radio_button_checked.imageset/ic_radio_button_checked_white_1x_ios_24dp.png b/remoting/ios/app/resources/Assets.xcassets/ic_radio_button_checked.imageset/ic_radio_button_checked_white_1x_ios_24dp.png
deleted file mode 100644
index f747f49..0000000
--- a/remoting/ios/app/resources/Assets.xcassets/ic_radio_button_checked.imageset/ic_radio_button_checked_white_1x_ios_24dp.png
+++ /dev/null
Binary files differ
diff --git a/remoting/ios/app/resources/Assets.xcassets/ic_radio_button_checked.imageset/ic_radio_button_checked_white_2x_ios_24dp.png b/remoting/ios/app/resources/Assets.xcassets/ic_radio_button_checked.imageset/ic_radio_button_checked_white_2x_ios_24dp.png
deleted file mode 100644
index ed6fa9e..0000000
--- a/remoting/ios/app/resources/Assets.xcassets/ic_radio_button_checked.imageset/ic_radio_button_checked_white_2x_ios_24dp.png
+++ /dev/null
Binary files differ
diff --git a/remoting/ios/app/resources/Assets.xcassets/ic_radio_button_checked.imageset/ic_radio_button_checked_white_3x_ios_24dp.png b/remoting/ios/app/resources/Assets.xcassets/ic_radio_button_checked.imageset/ic_radio_button_checked_white_3x_ios_24dp.png
deleted file mode 100644
index fd6d09a..0000000
--- a/remoting/ios/app/resources/Assets.xcassets/ic_radio_button_checked.imageset/ic_radio_button_checked_white_3x_ios_24dp.png
+++ /dev/null
Binary files differ
diff --git a/remoting/ios/app/resources/Assets.xcassets/ic_radio_button_unchecked.imageset/Contents.json b/remoting/ios/app/resources/Assets.xcassets/ic_radio_button_unchecked.imageset/Contents.json
deleted file mode 100644
index 7b5924a..0000000
--- a/remoting/ios/app/resources/Assets.xcassets/ic_radio_button_unchecked.imageset/Contents.json
+++ /dev/null
@@ -1,23 +0,0 @@
-{
-  "images" : [
-    {
-      "idiom" : "universal",
-      "filename" : "ic_radio_button_unchecked_white_1x_ios_24dp.png",
-      "scale" : "1x"
-    },
-    {
-      "idiom" : "universal",
-      "filename" : "ic_radio_button_unchecked_white_2x_ios_24dp.png",
-      "scale" : "2x"
-    },
-    {
-      "idiom" : "universal",
-      "filename" : "ic_radio_button_unchecked_white_3x_ios_24dp.png",
-      "scale" : "3x"
-    }
-  ],
-  "info" : {
-    "version" : 1,
-    "author" : "xcode"
-  }
-}
\ No newline at end of file
diff --git a/remoting/ios/app/resources/Assets.xcassets/ic_radio_button_unchecked.imageset/ic_radio_button_unchecked_white_1x_ios_24dp.png b/remoting/ios/app/resources/Assets.xcassets/ic_radio_button_unchecked.imageset/ic_radio_button_unchecked_white_1x_ios_24dp.png
deleted file mode 100644
index e74f040..0000000
--- a/remoting/ios/app/resources/Assets.xcassets/ic_radio_button_unchecked.imageset/ic_radio_button_unchecked_white_1x_ios_24dp.png
+++ /dev/null
Binary files differ
diff --git a/remoting/ios/app/resources/Assets.xcassets/ic_radio_button_unchecked.imageset/ic_radio_button_unchecked_white_2x_ios_24dp.png b/remoting/ios/app/resources/Assets.xcassets/ic_radio_button_unchecked.imageset/ic_radio_button_unchecked_white_2x_ios_24dp.png
deleted file mode 100644
index f1a967f..0000000
--- a/remoting/ios/app/resources/Assets.xcassets/ic_radio_button_unchecked.imageset/ic_radio_button_unchecked_white_2x_ios_24dp.png
+++ /dev/null
Binary files differ
diff --git a/remoting/ios/app/resources/Assets.xcassets/ic_radio_button_unchecked.imageset/ic_radio_button_unchecked_white_3x_ios_24dp.png b/remoting/ios/app/resources/Assets.xcassets/ic_radio_button_unchecked.imageset/ic_radio_button_unchecked_white_3x_ios_24dp.png
deleted file mode 100644
index d1c733d..0000000
--- a/remoting/ios/app/resources/Assets.xcassets/ic_radio_button_unchecked.imageset/ic_radio_button_unchecked_white_3x_ios_24dp.png
+++ /dev/null
Binary files differ
diff --git a/remoting/ios/app/resources/Assets.xcassets/ic_touch_app.imageset/Contents.json b/remoting/ios/app/resources/Assets.xcassets/ic_touch_app.imageset/Contents.json
deleted file mode 100644
index 577dee7..0000000
--- a/remoting/ios/app/resources/Assets.xcassets/ic_touch_app.imageset/Contents.json
+++ /dev/null
@@ -1,24 +0,0 @@
-{
-    "images": [
-        {
-            "filename": "ic_touch_app.png",
-            "idiom": "universal",
-            "scale": "1x"
-        },
-        {
-            "filename": "ic_touch_app_2x.png",
-            "idiom": "universal",
-            "scale": "2x"
-        },
-        {
-            "filename": "ic_touch_app_3x.png",
-            "idiom": "universal",
-            "scale": "3x"
-        }
-    ],
-    "info": {
-        "author": "xcode",
-        "template-rendering-intent": "template",
-        "version": 1
-    }
-}
diff --git a/remoting/ios/app/resources/Assets.xcassets/ic_touch_app.imageset/ic_touch_app.png b/remoting/ios/app/resources/Assets.xcassets/ic_touch_app.imageset/ic_touch_app.png
deleted file mode 100644
index 37b6c67..0000000
--- a/remoting/ios/app/resources/Assets.xcassets/ic_touch_app.imageset/ic_touch_app.png
+++ /dev/null
Binary files differ
diff --git a/remoting/ios/app/resources/Assets.xcassets/ic_touch_app.imageset/ic_touch_app_2x.png b/remoting/ios/app/resources/Assets.xcassets/ic_touch_app.imageset/ic_touch_app_2x.png
deleted file mode 100644
index e697790..0000000
--- a/remoting/ios/app/resources/Assets.xcassets/ic_touch_app.imageset/ic_touch_app_2x.png
+++ /dev/null
Binary files differ
diff --git a/remoting/ios/app/resources/Assets.xcassets/ic_touch_app.imageset/ic_touch_app_3x.png b/remoting/ios/app/resources/Assets.xcassets/ic_touch_app.imageset/ic_touch_app_3x.png
deleted file mode 100644
index 850daa3..0000000
--- a/remoting/ios/app/resources/Assets.xcassets/ic_touch_app.imageset/ic_touch_app_3x.png
+++ /dev/null
Binary files differ
diff --git a/remoting/ios/app/resources/BUILD.gn b/remoting/ios/app/resources/BUILD.gn
index 8e8b4552..69eb25e 100644
--- a/remoting/ios/app/resources/BUILD.gn
+++ b/remoting/ios/app/resources/BUILD.gn
@@ -3,73 +3,44 @@
 # found in the LICENSE file.
 
 import("//build/config/chrome_build.gni")
+import("//build/config/ios/imageset.gni")
 import("//build/config/ios/rules.gni")
 import("//remoting/build/config/remoting_build.gni")
 
-bundle_data("assets") {
+group("assets") {
+  public_deps = [
+    ":Background",
+    "//third_party/material_design_icons:ic_arrow_forward_white",
+    "//third_party/material_design_icons:ic_check_box_outline_blank_white",
+    "//third_party/material_design_icons:ic_check_box_white",
+    "//third_party/material_design_icons:ic_chevron_left_white_36pt",
+    "//third_party/material_design_icons:ic_close_white",
+    "//third_party/material_design_icons:ic_desktop_windows_white",
+    "//third_party/material_design_icons:ic_menu_white",
+    "//third_party/material_design_icons:ic_radio_button_checked_white",
+    "//third_party/material_design_icons:ic_radio_button_unchecked_white",
+    "//third_party/material_design_icons:ic_refresh_white",
+    "//third_party/material_design_icons:ic_settings_white",
+
+    # Note: those assets seems to be unreferenced by the code, according to
+    # grep. Maybe they are in downstream repository, so leaving them for now.
+    "//third_party/material_design_icons:ic_desktop_windows",
+    "//third_party/material_design_icons:ic_fullscreen",
+    "//third_party/material_design_icons:ic_fullscreen_exit",
+    "//third_party/material_design_icons:ic_mouse",
+
+    # Note: this imageset is incorrect (miss the Contents.json file) but it
+    # appears to be unused, so for the moment keep it commented out. See
+    # https://github.com/google/material-design-icons/issues/630 for progress.
+    # "//third_party/material_design_icons:ic_touch_app",
+  ]
+}
+
+imageset("Background") {
   sources = [
-    "Assets.xcassets/Back.imageset/Contents.json",
-    "Assets.xcassets/Back.imageset/ic_keyboard_arrow_left_white_1x_ios_36dp.png",
-    "Assets.xcassets/Back.imageset/ic_keyboard_arrow_left_white_2x_ios_36dp.png",
-    "Assets.xcassets/Back.imageset/ic_keyboard_arrow_left_white_3x_ios_36dp.png",
     "Assets.xcassets/Background.imageset/Contents.json",
     "Assets.xcassets/Background.imageset/bkg1.jpg",
     "Assets.xcassets/Background.imageset/bkg1_2x.jpg",
-    "Assets.xcassets/Settings.imageset/Contents.json",
-    "Assets.xcassets/Settings.imageset/ic_settings_white_1x_ios_24dp.png",
-    "Assets.xcassets/Settings.imageset/ic_settings_white_2x_ios_24dp.png",
-    "Assets.xcassets/Settings.imageset/ic_settings_white_3x_ios_24dp.png",
-    "Assets.xcassets/ic_arrow_forward_white.imageset/Contents.json",
-    "Assets.xcassets/ic_arrow_forward_white.imageset/ic_arrow_forward_white.png",
-    "Assets.xcassets/ic_arrow_forward_white.imageset/ic_arrow_forward_white_2x.png",
-    "Assets.xcassets/ic_arrow_forward_white.imageset/ic_arrow_forward_white_3x.png",
-    "Assets.xcassets/ic_check_box.imageset/Contents.json",
-    "Assets.xcassets/ic_check_box.imageset/ic_check_box_white_1x_ios_24dp.png",
-    "Assets.xcassets/ic_check_box.imageset/ic_check_box_white_2x_ios_24dp.png",
-    "Assets.xcassets/ic_check_box.imageset/ic_check_box_white_3x_ios_24dp.png",
-    "Assets.xcassets/ic_check_box_outline_blank.imageset/Contents.json",
-    "Assets.xcassets/ic_check_box_outline_blank.imageset/ic_check_box_outline_blank_white_1x_ios_24dp.png",
-    "Assets.xcassets/ic_check_box_outline_blank.imageset/ic_check_box_outline_blank_white_2x_ios_24dp.png",
-    "Assets.xcassets/ic_check_box_outline_blank.imageset/ic_check_box_outline_blank_white_3x_ios_24dp.png",
-    "Assets.xcassets/ic_desktop.imageset/Contents.json",
-    "Assets.xcassets/ic_desktop.imageset/ic_desktop.png",
-    "Assets.xcassets/ic_desktop.imageset/ic_desktop@2x.png",
-    "Assets.xcassets/ic_desktop.imageset/ic_desktop@3x.png",
-    "Assets.xcassets/ic_desktop_windows.imageset/Contents.json",
-    "Assets.xcassets/ic_desktop_windows.imageset/ic_desktop_windows.png",
-    "Assets.xcassets/ic_desktop_windows.imageset/ic_desktop_windows@2x.png",
-    "Assets.xcassets/ic_desktop_windows.imageset/ic_desktop_windows@3x.png",
-    "Assets.xcassets/ic_fullscreen.imageset/Contents.json",
-    "Assets.xcassets/ic_fullscreen.imageset/ic_fullscreen.png",
-    "Assets.xcassets/ic_fullscreen.imageset/ic_fullscreen@2x.png",
-    "Assets.xcassets/ic_fullscreen.imageset/ic_fullscreen@3x.png",
-    "Assets.xcassets/ic_fullscreen_exit.imageset/Contents.json",
-    "Assets.xcassets/ic_fullscreen_exit.imageset/ic_fullscreen_exit.png",
-    "Assets.xcassets/ic_fullscreen_exit.imageset/ic_fullscreen_exit@2x.png",
-    "Assets.xcassets/ic_fullscreen_exit.imageset/ic_fullscreen_exit@3x.png",
-    "Assets.xcassets/ic_menu.imageset/Contents.json",
-    "Assets.xcassets/ic_menu.imageset/ic_menu_white.png",
-    "Assets.xcassets/ic_menu.imageset/ic_menu_white_2x.png",
-    "Assets.xcassets/ic_menu.imageset/ic_menu_white_3x.png",
-    "Assets.xcassets/ic_mouse.imageset/Contents.json",
-    "Assets.xcassets/ic_mouse.imageset/ic_mouse.png",
-    "Assets.xcassets/ic_mouse.imageset/ic_mouse@2x.png",
-    "Assets.xcassets/ic_mouse.imageset/ic_mouse@3x.png",
-    "Assets.xcassets/ic_radio_button_checked.imageset/Contents.json",
-    "Assets.xcassets/ic_radio_button_checked.imageset/ic_radio_button_checked_white_1x_ios_24dp.png",
-    "Assets.xcassets/ic_radio_button_checked.imageset/ic_radio_button_checked_white_2x_ios_24dp.png",
-    "Assets.xcassets/ic_radio_button_checked.imageset/ic_radio_button_checked_white_3x_ios_24dp.png",
-    "Assets.xcassets/ic_radio_button_unchecked.imageset/Contents.json",
-    "Assets.xcassets/ic_radio_button_unchecked.imageset/ic_radio_button_unchecked_white_1x_ios_24dp.png",
-    "Assets.xcassets/ic_radio_button_unchecked.imageset/ic_radio_button_unchecked_white_2x_ios_24dp.png",
-    "Assets.xcassets/ic_radio_button_unchecked.imageset/ic_radio_button_unchecked_white_3x_ios_24dp.png",
-    "Assets.xcassets/ic_touch_app.imageset/Contents.json",
-    "Assets.xcassets/ic_touch_app.imageset/ic_touch_app.png",
-    "Assets.xcassets/ic_touch_app.imageset/ic_touch_app_2x.png",
-    "Assets.xcassets/ic_touch_app.imageset/ic_touch_app_3x.png",
-  ]
-  outputs = [
-    "{{bundle_resources_dir}}/{{source_file_part}}",
   ]
 }
 
@@ -77,37 +48,32 @@
 # the internal app's source set, that are already pulled-in by other deps of the
 # internal app. We will need to redesign these once crbug.com/734054 is
 # resolved.
-bundle_data("system_icons") {
-  sources = [
-    "system_icons.xcassets/ic_close.imageset/Contents.json",
-    "system_icons.xcassets/ic_close.imageset/ic_close_white_1x_ios_24dp.png",
-    "system_icons.xcassets/ic_close.imageset/ic_close_white_2x_ios_24dp.png",
-    "system_icons.xcassets/ic_close.imageset/ic_close_white_3x_ios_24dp.png",
-    "system_icons.xcassets/ic_refresh.imageset/Contents.json",
-    "system_icons.xcassets/ic_refresh.imageset/ic_refresh_white.png",
-    "system_icons.xcassets/ic_refresh.imageset/ic_refresh_white_2x.png",
-    "system_icons.xcassets/ic_refresh.imageset/ic_refresh_white_3x.png",
-  ]
-  outputs = [
-    "{{bundle_resources_dir}}/{{source_file_part}}",
+group("system_icons") {
+}
+
+group("launchscreen_assets") {
+  public_deps = [
+    ":launchscreen_app_logo",
+    ":launchscreen_brand_name",
   ]
 }
 
-bundle_data("launchscreen_assets") {
+imageset("launchscreen_app_logo") {
   sources = [
-    "launchscreen_images.xcassets/Contents.json",
     "launchscreen_images.xcassets/launchscreen_app_logo.imageset/Contents.json",
     "launchscreen_images.xcassets/launchscreen_app_logo.imageset/launchscreen_app_logo.png",
     "launchscreen_images.xcassets/launchscreen_app_logo.imageset/launchscreen_app_logo@2x.png",
     "launchscreen_images.xcassets/launchscreen_app_logo.imageset/launchscreen_app_logo@3x.png",
+  ]
+}
+
+imageset("launchscreen_brand_name") {
+  sources = [
     "launchscreen_images.xcassets/launchscreen_brand_name.imageset/Contents.json",
     "launchscreen_images.xcassets/launchscreen_brand_name.imageset/launchscreen_brand_name.png",
     "launchscreen_images.xcassets/launchscreen_brand_name.imageset/launchscreen_brand_name@2x.png",
     "launchscreen_images.xcassets/launchscreen_brand_name.imageset/launchscreen_brand_name@3x.png",
   ]
-  outputs = [
-    "{{bundle_resources_dir}}/{{source_file_part}}",
-  ]
 }
 
 bundle_data("remoting_icons") {
diff --git a/remoting/ios/app/resources/system_icons.xcassets/Contents.json b/remoting/ios/app/resources/system_icons.xcassets/Contents.json
deleted file mode 100644
index da4a164c..0000000
--- a/remoting/ios/app/resources/system_icons.xcassets/Contents.json
+++ /dev/null
@@ -1,6 +0,0 @@
-{
-  "info" : {
-    "version" : 1,
-    "author" : "xcode"
-  }
-}
\ No newline at end of file
diff --git a/remoting/ios/app/resources/system_icons.xcassets/ic_close.imageset/Contents.json b/remoting/ios/app/resources/system_icons.xcassets/ic_close.imageset/Contents.json
deleted file mode 100644
index c6315c9..0000000
--- a/remoting/ios/app/resources/system_icons.xcassets/ic_close.imageset/Contents.json
+++ /dev/null
@@ -1,23 +0,0 @@
-{
-  "images" : [
-    {
-      "idiom" : "universal",
-      "filename" : "ic_close_white_1x_ios_24dp.png",
-      "scale" : "1x"
-    },
-    {
-      "idiom" : "universal",
-      "filename" : "ic_close_white_2x_ios_24dp.png",
-      "scale" : "2x"
-    },
-    {
-      "idiom" : "universal",
-      "filename" : "ic_close_white_3x_ios_24dp.png",
-      "scale" : "3x"
-    }
-  ],
-  "info" : {
-    "version" : 1,
-    "author" : "xcode"
-  }
-}
\ No newline at end of file
diff --git a/remoting/ios/app/resources/system_icons.xcassets/ic_close.imageset/ic_close_white_1x_ios_24dp.png b/remoting/ios/app/resources/system_icons.xcassets/ic_close.imageset/ic_close_white_1x_ios_24dp.png
deleted file mode 100644
index af7f828..0000000
--- a/remoting/ios/app/resources/system_icons.xcassets/ic_close.imageset/ic_close_white_1x_ios_24dp.png
+++ /dev/null
Binary files differ
diff --git a/remoting/ios/app/resources/system_icons.xcassets/ic_close.imageset/ic_close_white_2x_ios_24dp.png b/remoting/ios/app/resources/system_icons.xcassets/ic_close.imageset/ic_close_white_2x_ios_24dp.png
deleted file mode 100644
index b7c7ffd0..0000000
--- a/remoting/ios/app/resources/system_icons.xcassets/ic_close.imageset/ic_close_white_2x_ios_24dp.png
+++ /dev/null
Binary files differ
diff --git a/remoting/ios/app/resources/system_icons.xcassets/ic_close.imageset/ic_close_white_3x_ios_24dp.png b/remoting/ios/app/resources/system_icons.xcassets/ic_close.imageset/ic_close_white_3x_ios_24dp.png
deleted file mode 100644
index 6b717e0d..0000000
--- a/remoting/ios/app/resources/system_icons.xcassets/ic_close.imageset/ic_close_white_3x_ios_24dp.png
+++ /dev/null
Binary files differ
diff --git a/remoting/ios/app/resources/system_icons.xcassets/ic_refresh.imageset/Contents.json b/remoting/ios/app/resources/system_icons.xcassets/ic_refresh.imageset/Contents.json
deleted file mode 100644
index 47cd579..0000000
--- a/remoting/ios/app/resources/system_icons.xcassets/ic_refresh.imageset/Contents.json
+++ /dev/null
@@ -1,23 +0,0 @@
-{
-  "images" : [
-    {
-      "idiom" : "universal",
-      "filename" : "ic_refresh_white.png",
-      "scale" : "1x"
-    },
-    {
-      "idiom" : "universal",
-      "filename" : "ic_refresh_white_2x.png",
-      "scale" : "2x"
-    },
-    {
-      "idiom" : "universal",
-      "filename" : "ic_refresh_white_3x.png",
-      "scale" : "3x"
-    }
-  ],
-  "info" : {
-    "version" : 1,
-    "author" : "xcode"
-  }
-}
\ No newline at end of file
diff --git a/remoting/ios/app/resources/system_icons.xcassets/ic_refresh.imageset/ic_refresh_white.png b/remoting/ios/app/resources/system_icons.xcassets/ic_refresh.imageset/ic_refresh_white.png
deleted file mode 100644
index 97e42b5..0000000
--- a/remoting/ios/app/resources/system_icons.xcassets/ic_refresh.imageset/ic_refresh_white.png
+++ /dev/null
Binary files differ
diff --git a/remoting/ios/app/resources/system_icons.xcassets/ic_refresh.imageset/ic_refresh_white_2x.png b/remoting/ios/app/resources/system_icons.xcassets/ic_refresh.imageset/ic_refresh_white_2x.png
deleted file mode 100644
index 1989184..0000000
--- a/remoting/ios/app/resources/system_icons.xcassets/ic_refresh.imageset/ic_refresh_white_2x.png
+++ /dev/null
Binary files differ
diff --git a/remoting/ios/app/resources/system_icons.xcassets/ic_refresh.imageset/ic_refresh_white_3x.png b/remoting/ios/app/resources/system_icons.xcassets/ic_refresh.imageset/ic_refresh_white_3x.png
deleted file mode 100644
index a21d0ec..0000000
--- a/remoting/ios/app/resources/system_icons.xcassets/ic_refresh.imageset/ic_refresh_white_3x.png
+++ /dev/null
Binary files differ
diff --git a/testing/buildbot/chromium.fyi.json b/testing/buildbot/chromium.fyi.json
index d9d6dbc..f5ebb724 100644
--- a/testing/buildbot/chromium.fyi.json
+++ b/testing/buildbot/chromium.fyi.json
@@ -2089,12 +2089,6 @@
         "swarming": {
           "can_use_on_swarming_builders": false
         },
-        "test": "message_center_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": false
-        },
         "test": "midi_unittests"
       },
       {
@@ -2363,12 +2357,6 @@
         "swarming": {
           "can_use_on_swarming_builders": false
         },
-        "test": "message_center_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": false
-        },
         "test": "midi_unittests"
       },
       {
@@ -2657,12 +2645,6 @@
         "swarming": {
           "can_use_on_swarming_builders": false
         },
-        "test": "message_center_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": false
-        },
         "test": "midi_unittests"
       },
       {
@@ -5268,12 +5250,6 @@
         "swarming": {
           "can_use_on_swarming_builders": true
         },
-        "test": "message_center_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
         "test": "midi_unittests"
       },
       {
@@ -5593,12 +5569,6 @@
         "swarming": {
           "can_use_on_swarming_builders": true
         },
-        "test": "message_center_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
         "test": "midi_unittests"
       },
       {
diff --git a/testing/buildbot/chromium.mac.json b/testing/buildbot/chromium.mac.json
index 2773bb8..dfcf46b 100644
--- a/testing/buildbot/chromium.mac.json
+++ b/testing/buildbot/chromium.mac.json
@@ -253,12 +253,6 @@
         "swarming": {
           "can_use_on_swarming_builders": true
         },
-        "test": "message_center_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
         "test": "midi_unittests"
       },
       {
@@ -687,12 +681,6 @@
         "swarming": {
           "can_use_on_swarming_builders": true
         },
-        "test": "message_center_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
         "test": "midi_unittests"
       },
       {
@@ -1141,12 +1129,6 @@
         "swarming": {
           "can_use_on_swarming_builders": true
         },
-        "test": "message_center_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
         "test": "midi_unittests"
       },
       {
@@ -1559,12 +1541,6 @@
         "swarming": {
           "can_use_on_swarming_builders": true
         },
-        "test": "message_center_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
         "test": "midi_unittests"
       },
       {
@@ -1958,12 +1934,6 @@
         "swarming": {
           "can_use_on_swarming_builders": true
         },
-        "test": "message_center_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
         "test": "midi_unittests"
       },
       {
diff --git a/third_party/WebKit/LayoutTests/FlagExpectations/enable-blink-features=LayoutNG b/third_party/WebKit/LayoutTests/FlagExpectations/enable-blink-features=LayoutNG
index d9455d02..e61a712 100644
--- a/third_party/WebKit/LayoutTests/FlagExpectations/enable-blink-features=LayoutNG
+++ b/third_party/WebKit/LayoutTests/FlagExpectations/enable-blink-features=LayoutNG
@@ -123,7 +123,7 @@
 crbug.com/591099 accessibility/input-file-causes-crash.html [ Crash Failure ]
 crbug.com/591099 accessibility/input-image-alt.html [ Failure ]
 crbug.com/591099 accessibility/input-mixed.html [ Crash Pass ]
-crbug.com/591099 accessibility/input-type-password-value-and-selection.html [ Crash Timeout ]
+crbug.com/591099 accessibility/input-type-password-value-and-selection.html [ Crash Failure Timeout ]
 crbug.com/591099 accessibility/input-type-range-aria-value.html [ Failure ]
 crbug.com/591099 accessibility/input-type-range-value-change-event.html [ Crash Pass ]
 crbug.com/591099 accessibility/input-type-range-value-change.html [ Failure ]
@@ -264,14 +264,14 @@
 crbug.com/591099 animations/interpolation/caret-color-interpolation.html [ Crash Pass ]
 crbug.com/591099 animations/interpolation/clip-interpolation.html [ Crash Pass ]
 crbug.com/591099 animations/interpolation/color-interpolation.html [ Crash Pass ]
-crbug.com/591099 animations/interpolation/filter-interpolation.html [ Crash Pass ]
+crbug.com/591099 animations/interpolation/filter-interpolation.html [ Crash Pass Timeout ]
 crbug.com/591099 animations/interpolation/font-size-adjust-interpolation.html [ Crash Pass ]
 crbug.com/591099 animations/interpolation/font-size-interpolation-unset.html [ Crash Pass ]
 crbug.com/591099 animations/interpolation/font-size-interpolation.html [ Crash Pass ]
 crbug.com/591099 animations/interpolation/font-size-zoom-interpolation.html [ Crash Pass ]
 crbug.com/591099 animations/interpolation/font-weight-interpolation.html [ Crash Pass ]
 crbug.com/591099 animations/interpolation/height-interpolation.html [ Crash Pass ]
-crbug.com/591099 animations/interpolation/line-height-interpolation.html [ Crash Timeout ]
+crbug.com/591099 animations/interpolation/line-height-interpolation.html [ Crash Pass Timeout ]
 crbug.com/591099 animations/interpolation/margin-interpolation.html [ Crash Pass ]
 crbug.com/591099 animations/interpolation/max-height-interpolation.html [ Crash Pass ]
 crbug.com/591099 animations/interpolation/min-height-interpolation.html [ Crash Pass ]
@@ -747,6 +747,7 @@
 crbug.com/591099 compositing/overflow/remove-overflow-crash2.html [ Failure ]
 crbug.com/591099 compositing/overflow/reparented-scrollbars-non-sc-anc.html [ Failure ]
 crbug.com/591099 compositing/overflow/resize-painting.html [ Failure ]
+crbug.com/591099 compositing/overflow/respect-clip-for-non-composited-scrollers-when-prefering-compositing-over-lcd-text.html [ Failure ]
 crbug.com/591099 compositing/overflow/scaled-mask.html [ Failure ]
 crbug.com/591099 compositing/overflow/scaled-overflow.html [ Failure ]
 crbug.com/591099 compositing/overflow/scroll-ancestor-update.html [ Failure ]
@@ -5708,7 +5709,7 @@
 crbug.com/591099 external/wpt/media-source/mediasource-play.html [ Crash Pass ]
 crbug.com/591099 external/wpt/media-source/mediasource-preload.html [ Crash Pass ]
 crbug.com/591099 external/wpt/media-source/mediasource-redundant-seek.html [ Crash Pass ]
-crbug.com/591099 external/wpt/media-source/mediasource-remove.html [ Crash Pass ]
+crbug.com/591099 external/wpt/media-source/mediasource-remove.html [ Crash Failure Pass ]
 crbug.com/591099 external/wpt/media-source/mediasource-removesourcebuffer.html [ Crash Pass ]
 crbug.com/591099 external/wpt/media-source/mediasource-seek-beyond-duration.html [ Crash Pass ]
 crbug.com/591099 external/wpt/media-source/mediasource-seek-during-pending-seek.html [ Crash Pass ]
@@ -11687,7 +11688,7 @@
 crbug.com/591099 fast/harness/perftests/perf-runner-compute-statistics.html [ Failure ]
 crbug.com/591099 fast/harness/perftests/runs-per-second-iterations.html [ Failure ]
 crbug.com/591099 fast/harness/perftests/runs-per-second-log.html [ Failure ]
-crbug.com/591099 fast/harness/results.html [ Crash Pass ]
+crbug.com/591099 fast/harness/results.html [ Crash Pass Timeout ]
 crbug.com/591099 fast/harness/should-be-now.html [ Failure ]
 crbug.com/591099 fast/harness/user-preferred-language.html [ Crash Failure ]
 crbug.com/591099 fast/hidpi/broken-image-icon-hidpi.html [ Crash Failure Pass ]
@@ -14294,7 +14295,7 @@
 crbug.com/591099 geolocation-api/watchPosition-unique.html [ Failure ]
 crbug.com/591099 geolocation-api/window-close-crash.html [ Failure ]
 crbug.com/591099 hittesting/border-hittest-inlineFlowBox.html [ Failure ]
-crbug.com/591099 hittesting/border-hittest-with-image-fallback.html [ Crash Failure ]
+crbug.com/591099 hittesting/border-hittest-with-image-fallback.html [ Crash Failure Pass ]
 crbug.com/591099 hittesting/border-hittest.html [ Failure ]
 crbug.com/591099 hittesting/border-radius-hittest.html [ Failure ]
 crbug.com/591099 hittesting/culled-inline-crash.html [ Failure ]
@@ -15105,7 +15106,7 @@
 crbug.com/591099 http/tests/media/remove-while-loading.html [ Crash Pass ]
 crbug.com/591099 http/tests/media/text-served-as-text.html [ Crash Pass ]
 crbug.com/591099 http/tests/media/video-buffered-range-contains-currentTime.html [ Failure Pass ]
-crbug.com/591099 http/tests/media/video-buffered.html [ Crash Pass ]
+crbug.com/591099 http/tests/media/video-buffered.html [ Crash Failure Pass ]
 crbug.com/591099 http/tests/media/video-controls-download-button-displayed.html [ Crash Pass ]
 crbug.com/591099 http/tests/media/video-controls-download-button-not-displayed-hide-download-ui.html [ Crash Pass ]
 crbug.com/591099 http/tests/media/video-controls-download-button-not-displayed-mediastream.html [ Crash Pass ]
@@ -15694,7 +15695,9 @@
 crbug.com/591099 http/tests/security/suborigins/suborigin-unsafe-postmessage-send.html [ Crash Pass ]
 crbug.com/591099 http/tests/security/suborigins/suborigin-valid-names.html [ Crash Pass ]
 crbug.com/591099 http/tests/security/suborigins/suborigin-valid-options.html [ Crash Pass ]
+crbug.com/591099 http/tests/security/subresourceIntegrity/clear-integrity-attribute.html [ Failure ]
 crbug.com/591099 http/tests/security/subresourceIntegrity/integrity-attribute.html [ Failure ]
+crbug.com/591099 http/tests/security/subresourceIntegrity/shared-with-xhtml.html [ Failure ]
 crbug.com/591099 http/tests/security/subresourceIntegrity/subresource-integrity-not-enforced.html [ Crash Pass ]
 crbug.com/591099 http/tests/security/subresourceIntegrity/subresource-integrity-style-allowed.html [ Failure ]
 crbug.com/591099 http/tests/security/subresourceIntegrity/subresource-integrity-style-blocked.html [ Failure ]
@@ -16011,7 +16014,7 @@
 crbug.com/591099 http/tests/xmlhttprequest/detaching-frame.html [ Crash Failure ]
 crbug.com/591099 http/tests/xmlhttprequest/docLoaderFrame.html [ Failure ]
 crbug.com/591099 http/tests/xmlhttprequest/duplicate-revalidation-reload.html [ Failure ]
-crbug.com/591099 http/tests/xmlhttprequest/encode-request-url-2.html [ Crash Failure ]
+crbug.com/591099 http/tests/xmlhttprequest/encode-request-url-2.html [ Crash Failure Timeout ]
 crbug.com/591099 http/tests/xmlhttprequest/encode-request-url.html [ Crash Failure ]
 crbug.com/591099 http/tests/xmlhttprequest/encoding-send-latin-1.html [ Failure ]
 crbug.com/591099 http/tests/xmlhttprequest/event-listener-gc.html [ Failure ]
@@ -16624,7 +16627,7 @@
 crbug.com/591099 inspector/curl-command.html [ Failure ]
 crbug.com/591099 inspector/database-table-name-excaping.html [ Failure ]
 crbug.com/591099 inspector/device-mode/device-mode-responsive.html [ Crash Failure ]
-crbug.com/591099 inspector/device-mode/device-mode-switching-devices.html [ Crash Failure ]
+crbug.com/591099 inspector/device-mode/device-mode-switching-devices.html [ Crash Failure Timeout ]
 crbug.com/591099 inspector/device-mode/forced-viewport-unobserved.html [ Crash Failure ]
 crbug.com/591099 inspector/diff-module.html [ Failure ]
 crbug.com/591099 inspector/domdebugger/domdebugger-getEventListeners.html [ Crash Failure Timeout ]
@@ -16674,7 +16677,7 @@
 crbug.com/591099 inspector/elements/edit/set-outer-html-for-xhtml.xhtml [ Crash Failure ]
 crbug.com/591099 inspector/elements/edit/set-outer-html.html [ Crash Failure ]
 crbug.com/591099 inspector/elements/edit/shadow-dom-modify-chardata.html [ Crash Failure ]
-crbug.com/591099 inspector/elements/edit/switch-panels-while-editing-as-html.html [ Crash Failure ]
+crbug.com/591099 inspector/elements/edit/switch-panels-while-editing-as-html.html [ Crash Failure Timeout ]
 crbug.com/591099 inspector/elements/edit/undo-dom-edits-2.html [ Crash Failure ]
 crbug.com/591099 inspector/elements/edit/undo-dom-edits.html [ Crash Failure Timeout ]
 crbug.com/591099 inspector/elements/edit/undo-set-outer-html-2.html [ Crash Failure ]
@@ -16763,7 +16766,7 @@
 crbug.com/591099 inspector/elements/styles-1/edit-value-inside-property.html [ Crash Failure ]
 crbug.com/591099 inspector/elements/styles-1/edit-value-url-with-color.html [ Crash Failure ]
 crbug.com/591099 inspector/elements/styles-1/edit-value-with-trimmed-url.html [ Crash Failure Timeout ]
-crbug.com/591099 inspector/elements/styles-1/empty-background-url.html [ Crash Failure ]
+crbug.com/591099 inspector/elements/styles-1/empty-background-url.html [ Crash Failure Timeout ]
 crbug.com/591099 inspector/elements/styles-1/filter-matched-styles.html [ Crash Failure ]
 crbug.com/591099 inspector/elements/styles-2/add-import-rule.html [ Crash Pass ]
 crbug.com/591099 inspector/elements/styles-2/cssom-shorthand-important.html [ Crash Failure ]
@@ -16863,7 +16866,7 @@
 crbug.com/591099 inspector/elements/styles/updates-during-dom-traversal.html [ Crash Failure ]
 crbug.com/591099 inspector/elements/styles/updates-throttled.html [ Crash Failure ]
 crbug.com/591099 inspector/elements/styles/url-color-swatch.html [ Crash Failure ]
-crbug.com/591099 inspector/elements/styles/url-multiple-collapsing.html [ Crash Failure ]
+crbug.com/591099 inspector/elements/styles/url-multiple-collapsing.html [ Crash Failure Timeout ]
 crbug.com/591099 inspector/elements/user-properties.html [ Crash Failure ]
 crbug.com/591099 inspector/evaluate-in-page.html [ Failure ]
 crbug.com/591099 inspector/file-reader-with-network-panel.html [ Failure ]
@@ -17566,7 +17569,7 @@
 crbug.com/591099 media/track/track-cues-pause-on-exit.html [ Crash Pass ]
 crbug.com/591099 media/track/track-cues-seeking.html [ Crash Pass ]
 crbug.com/591099 media/track/track-cues-sorted-before-dispatch.html [ Crash Pass ]
-crbug.com/591099 media/track/track-default-attribute.html [ Crash Failure Timeout ]
+crbug.com/591099 media/track/track-default-attribute.html [ Crash Failure Pass Timeout ]
 crbug.com/591099 media/track/track-delete-during-setup.html [ Crash Pass ]
 crbug.com/591099 media/track/track-disabled.html [ Crash Pass ]
 crbug.com/591099 media/track/track-element-load-event.html [ Crash Pass ]
@@ -18377,6 +18380,7 @@
 crbug.com/591099 paint/invalidation/window-resize-vertical-writing-mode.html [ Failure ]
 crbug.com/591099 paint/invalidation/window-resize-viewport-percent.html [ Failure Pass ]
 crbug.com/591099 paint/masks/fieldset-mask.html [ Failure ]
+crbug.com/591099 paint/overflow/composited-rounded-clip-floating-element.html [ Failure ]
 crbug.com/591099 paint/overflow/non-composited-fixed-position-descendant.html [ Failure ]
 crbug.com/591099 paint/pagination/pagination-change-clip-crash.html [ Failure ]
 crbug.com/591099 paint/printing/print-box-shadow.html [ Failure ]
@@ -18482,18 +18486,18 @@
 crbug.com/591099 pointer-lock/pointerlockchange-event-on-lock-lost.html [ Failure ]
 crbug.com/591099 pointer-lock/pointerlockchange-pointerlockerror-events.html [ Failure ]
 crbug.com/591099 pointer-lock/pointerlockelement-null-when-pending.html [ Failure ]
-crbug.com/591099 presentation/presentation-controller-close-connection.html [ Crash Timeout ]
+crbug.com/591099 presentation/presentation-controller-close-connection.html [ Crash Pass Timeout ]
 crbug.com/591099 presentation/presentation-controller-connection-closed-by-receiver.html [ Crash Pass Timeout ]
 crbug.com/591099 presentation/presentation-controller-terminate-connection.html [ Crash Pass Timeout ]
 crbug.com/591099 presentation/presentation-navigation-multipleurls.html [ Crash Pass ]
 crbug.com/591099 presentation/presentation-navigation.html [ Crash Pass ]
 crbug.com/591099 presentation/presentation-receiver-terminate-connection.html [ Crash Pass Timeout ]
-crbug.com/591099 presentation/presentation-reconnect.html [ Crash Timeout ]
+crbug.com/591099 presentation/presentation-reconnect.html [ Crash Pass Timeout ]
 crbug.com/591099 presentation/presentation-request-iframe-default-success.html [ Crash Pass ]
 crbug.com/591099 presentation/presentation-request-iframe-sandbox-error.html [ Crash Pass ]
 crbug.com/591099 presentation/presentation-request-iframe-sandbox-success.html [ Crash Pass ]
 crbug.com/591099 presentation/presentation-start-error.html [ Crash Pass Timeout ]
-crbug.com/591099 presentation/presentation-start.html [ Crash Timeout ]
+crbug.com/591099 presentation/presentation-start.html [ Crash Pass Timeout ]
 crbug.com/591099 presentation/presentationconnectionavailableevent-ctor-mock.html [ Crash Pass Timeout ]
 crbug.com/591099 printing/absolute-position-headers-and-footers.html [ Failure ]
 crbug.com/591099 printing/absolute-positioned.html [ Failure ]
@@ -21330,6 +21334,7 @@
 crbug.com/591099 virtual/prefer_compositing_to_lcd_text/compositing/overflow/remove-overflow-crash2.html [ Failure ]
 crbug.com/591099 virtual/prefer_compositing_to_lcd_text/compositing/overflow/reparented-scrollbars-non-sc-anc.html [ Failure ]
 crbug.com/591099 virtual/prefer_compositing_to_lcd_text/compositing/overflow/resize-painting.html [ Failure ]
+crbug.com/591099 virtual/prefer_compositing_to_lcd_text/compositing/overflow/respect-clip-for-non-composited-scrollers-when-prefering-compositing-over-lcd-text.html [ Failure ]
 crbug.com/591099 virtual/prefer_compositing_to_lcd_text/compositing/overflow/scaled-mask.html [ Failure ]
 crbug.com/591099 virtual/prefer_compositing_to_lcd_text/compositing/overflow/scaled-overflow.html [ Failure ]
 crbug.com/591099 virtual/prefer_compositing_to_lcd_text/compositing/overflow/scroll-ancestor-update.html [ Failure ]
@@ -21639,7 +21644,7 @@
 crbug.com/591099 virtual/threaded/animations/interpolation/background-image-interpolation.html [ Crash Timeout ]
 crbug.com/591099 virtual/threaded/animations/interpolation/background-position-interpolation.html [ Crash Pass ]
 crbug.com/591099 virtual/threaded/animations/interpolation/background-position-origin-interpolation.html [ Crash Pass ]
-crbug.com/591099 virtual/threaded/animations/interpolation/background-size-interpolation.html [ Crash Pass ]
+crbug.com/591099 virtual/threaded/animations/interpolation/background-size-interpolation.html [ Crash Pass Timeout ]
 crbug.com/591099 virtual/threaded/animations/interpolation/border-color-interpolation.html [ Crash Pass ]
 crbug.com/591099 virtual/threaded/animations/interpolation/border-image-outset-interpolation.html [ Crash Pass ]
 crbug.com/591099 virtual/threaded/animations/interpolation/border-image-slice-interpolation.html [ Crash Pass ]
@@ -21791,7 +21796,7 @@
 crbug.com/591099 virtual/threaded/animations/svg-attribute-composition/svg-slope-composition.html [ Crash Pass ]
 crbug.com/591099 virtual/threaded/animations/svg-attribute-composition/svg-specularConstant-composition.html [ Crash Pass ]
 crbug.com/591099 virtual/threaded/animations/svg-attribute-composition/svg-specularExponent-composition.html [ Crash Pass ]
-crbug.com/591099 virtual/threaded/animations/svg-attribute-composition/svg-startOffset-composition.html [ Crash Pass ]
+crbug.com/591099 virtual/threaded/animations/svg-attribute-composition/svg-startOffset-composition.html [ Crash Failure Pass ]
 crbug.com/591099 virtual/threaded/animations/svg-attribute-composition/svg-stdDeviation-composition.html [ Crash Pass ]
 crbug.com/591099 virtual/threaded/animations/svg-attribute-composition/svg-surfaceScale-composition.html [ Crash Pass ]
 crbug.com/591099 virtual/threaded/animations/svg-attribute-composition/svg-tableValues-composition.html [ Crash Pass ]
diff --git a/third_party/WebKit/LayoutTests/FlagExpectations/enable-features=NetworkService b/third_party/WebKit/LayoutTests/FlagExpectations/enable-features=NetworkService
index 83e8515..18a99ab7 100644
--- a/third_party/WebKit/LayoutTests/FlagExpectations/enable-features=NetworkService
+++ b/third_party/WebKit/LayoutTests/FlagExpectations/enable-features=NetworkService
@@ -263,6 +263,7 @@
 Bug(none) external/wpt/beacon/headers/header-referrer-strict-origin.https.html [ Failure Timeout ]
 Bug(none) external/wpt/beacon/headers/header-referrer-unsafe-url.https.html [ Failure Timeout ]
 Bug(none) external/wpt/clear-site-data/navigation.https.html [ Timeout ]
+Bug(none) external/wpt/clipboard-apis [ Timeout ]
 Bug(none) external/wpt/content-security-policy/child-src/child-src-about-blank-allowed-by-default.sub.html [ Failure Timeout ]
 Bug(none) external/wpt/content-security-policy/child-src/child-src-about-blank-allowed-by-scheme.sub.html [ Failure Timeout ]
 Bug(none) external/wpt/content-security-policy/child-src/child-src-allowed.sub.html [ Failure Timeout ]
@@ -3291,7 +3292,7 @@
 Bug(none) vr/getEyeParameters_match.html [ Timeout ]
 Bug(none) vr/getFrameData_noupdate.html [ Timeout ]
 Bug(none) vr/getFrameData_oneframeupdate.html [ Timeout ]
-Bug(none) vr/getFrameData_samewithinframe.html [ Timeout ]
+Bug(none) vr/getFrameData_samewithinframe.html [ Failure Timeout ]
 Bug(none) vr/getLayers_notpresenting.html [ Timeout ]
 Bug(none) vr/getLayers_presenting.html [ Timeout ]
 Bug(none) vr/getLayers_presenting_nondefaultbounds.html [ Timeout ]
diff --git a/third_party/WebKit/LayoutTests/MSANExpectations b/third_party/WebKit/LayoutTests/MSANExpectations
index d58330c..e7a990da 100644
--- a/third_party/WebKit/LayoutTests/MSANExpectations
+++ b/third_party/WebKit/LayoutTests/MSANExpectations
@@ -39,7 +39,6 @@
 
 crbug.com/671556 [ Linux ] virtual/mojo-loading/http/tests/security/xssAuditor/report-script-tag-replace-state.html [ Timeout Pass ]
 crbug.com/671556 [ Linux ] virtual/mojo-loading/http/tests/security/xssAuditor/report-script-tag.html [ Timeout Pass ]
-crbug.com/736802 [ Linux ] virtual/mojo-loading/http/tests/fetch/serviceworker-proxied/thorough/cors-preflight2-other-https.html [ Crash ]
 
 crbug.com/736370 [ Linux ] external/wpt/editing/run/removeformat.html [ Timeout ]
 crbug.com/736554 [ Linux ] external/wpt/IndexedDB/nested-cloning-large-multiple.html [ Timeout ]
diff --git a/third_party/WebKit/LayoutTests/external/wpt/webrtc/RTCPeerConnection-idl-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/webrtc/RTCPeerConnection-idl-expected.txt
index a1bfa22..9627ae4 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/webrtc/RTCPeerConnection-idl-expected.txt
+++ b/third_party/WebKit/LayoutTests/external/wpt/webrtc/RTCPeerConnection-idl-expected.txt
@@ -1,5 +1,5 @@
 This is a testharness.js-based test.
-Found 116 tests; 66 PASS, 50 FAIL, 0 TIMEOUT, 0 NOTRUN.
+Found 116 tests; 72 PASS, 44 FAIL, 0 TIMEOUT, 0 NOTRUN.
 PASS EventTarget interface: existence and properties of interface object 
 PASS EventTarget interface object length 
 PASS EventTarget interface object name 
@@ -54,8 +54,8 @@
 PASS RTCPeerConnection interface: operation generateCertificate(AlgorithmIdentifier) 
 PASS RTCPeerConnection interface: operation getSenders() 
 PASS RTCPeerConnection interface: operation getReceivers() 
-FAIL RTCPeerConnection interface: operation addTrack(MediaStreamTrack,MediaStream) assert_own_property: interface prototype object missing non-static operation expected property "addTrack" missing
-FAIL RTCPeerConnection interface: operation removeTrack(RTCRtpSender) assert_own_property: interface prototype object missing non-static operation expected property "removeTrack" missing
+PASS RTCPeerConnection interface: operation addTrack(MediaStreamTrack,MediaStream) 
+PASS RTCPeerConnection interface: operation removeTrack(RTCRtpSender) 
 FAIL RTCPeerConnection interface: attribute ontrack assert_true: The prototype object must have a property "ontrack" expected true got false
 PASS RTCPeerConnection interface: operation createDataChannel(DOMString,RTCDataChannelInit) 
 PASS RTCPeerConnection interface: attribute ondatachannel 
@@ -118,10 +118,10 @@
 FAIL RTCPeerConnection interface: calling generateCertificate(AlgorithmIdentifier) on pc with too few arguments must throw TypeError assert_unreached: Throws "TypeError: Cannot read property 'apply' of undefined" instead of rejecting promise Reached unreachable code
 PASS RTCPeerConnection interface: pc must inherit property "getSenders" with the proper type (30) 
 PASS RTCPeerConnection interface: pc must inherit property "getReceivers" with the proper type (31) 
-FAIL RTCPeerConnection interface: pc must inherit property "addTrack" with the proper type (32) assert_inherits: property "addTrack" not found in prototype chain
-FAIL RTCPeerConnection interface: calling addTrack(MediaStreamTrack,MediaStream) on pc with too few arguments must throw TypeError assert_inherits: property "addTrack" not found in prototype chain
-FAIL RTCPeerConnection interface: pc must inherit property "removeTrack" with the proper type (33) assert_inherits: property "removeTrack" not found in prototype chain
-FAIL RTCPeerConnection interface: calling removeTrack(RTCRtpSender) on pc with too few arguments must throw TypeError assert_inherits: property "removeTrack" not found in prototype chain
+PASS RTCPeerConnection interface: pc must inherit property "addTrack" with the proper type (32) 
+PASS RTCPeerConnection interface: calling addTrack(MediaStreamTrack,MediaStream) on pc with too few arguments must throw TypeError 
+PASS RTCPeerConnection interface: pc must inherit property "removeTrack" with the proper type (33) 
+PASS RTCPeerConnection interface: calling removeTrack(RTCRtpSender) on pc with too few arguments must throw TypeError 
 FAIL RTCPeerConnection interface: pc must inherit property "ontrack" with the proper type (34) assert_inherits: property "ontrack" not found in prototype chain
 PASS RTCPeerConnection interface: pc must inherit property "createDataChannel" with the proper type (35) 
 PASS RTCPeerConnection interface: calling createDataChannel(DOMString,RTCDataChannelInit) on pc with too few arguments must throw TypeError 
diff --git a/third_party/WebKit/LayoutTests/external/wpt/webrtc/interfaces-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/webrtc/interfaces-expected.txt
index 49a5a994..4d429cd7 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/webrtc/interfaces-expected.txt
+++ b/third_party/WebKit/LayoutTests/external/wpt/webrtc/interfaces-expected.txt
@@ -1,5 +1,5 @@
 This is a testharness.js-based test.
-Found 498 tests; 188 PASS, 310 FAIL, 0 TIMEOUT, 0 NOTRUN.
+Found 498 tests; 194 PASS, 304 FAIL, 0 TIMEOUT, 0 NOTRUN.
 PASS Main test driver 
 PASS Test driver for asyncInitCertificate 
 FAIL Test driver for asyncInitTransports assert_unreached: Failed to run asyncInitTransports: OperationError: TEST_ERROR Reached unreachable code
@@ -61,8 +61,8 @@
 PASS RTCPeerConnection interface: operation getSenders() 
 PASS RTCPeerConnection interface: operation getReceivers() 
 FAIL RTCPeerConnection interface: operation getTransceivers() assert_own_property: interface prototype object missing non-static operation expected property "getTransceivers" missing
-FAIL RTCPeerConnection interface: operation addTrack(MediaStreamTrack,MediaStream) assert_own_property: interface prototype object missing non-static operation expected property "addTrack" missing
-FAIL RTCPeerConnection interface: operation removeTrack(RTCRtpSender) assert_own_property: interface prototype object missing non-static operation expected property "removeTrack" missing
+PASS RTCPeerConnection interface: operation addTrack(MediaStreamTrack,MediaStream) 
+PASS RTCPeerConnection interface: operation removeTrack(RTCRtpSender) 
 FAIL RTCPeerConnection interface: operation addTransceiver([object Object],[object Object],RTCRtpTransceiverInit) assert_own_property: interface prototype object missing non-static operation expected property "addTransceiver" missing
 FAIL RTCPeerConnection interface: attribute ontrack assert_true: The prototype object must have a property "ontrack" expected true got false
 FAIL RTCPeerConnection interface: attribute sctp assert_true: The prototype object must have a property "sctp" expected true got false
@@ -125,10 +125,10 @@
 PASS RTCPeerConnection interface: new RTCPeerConnection() must inherit property "getSenders" with the proper type (34) 
 PASS RTCPeerConnection interface: new RTCPeerConnection() must inherit property "getReceivers" with the proper type (35) 
 FAIL RTCPeerConnection interface: new RTCPeerConnection() must inherit property "getTransceivers" with the proper type (36) assert_inherits: property "getTransceivers" not found in prototype chain
-FAIL RTCPeerConnection interface: new RTCPeerConnection() must inherit property "addTrack" with the proper type (37) assert_inherits: property "addTrack" not found in prototype chain
-FAIL RTCPeerConnection interface: calling addTrack(MediaStreamTrack,MediaStream) on new RTCPeerConnection() with too few arguments must throw TypeError assert_inherits: property "addTrack" not found in prototype chain
-FAIL RTCPeerConnection interface: new RTCPeerConnection() must inherit property "removeTrack" with the proper type (38) assert_inherits: property "removeTrack" not found in prototype chain
-FAIL RTCPeerConnection interface: calling removeTrack(RTCRtpSender) on new RTCPeerConnection() with too few arguments must throw TypeError assert_inherits: property "removeTrack" not found in prototype chain
+PASS RTCPeerConnection interface: new RTCPeerConnection() must inherit property "addTrack" with the proper type (37) 
+PASS RTCPeerConnection interface: calling addTrack(MediaStreamTrack,MediaStream) on new RTCPeerConnection() with too few arguments must throw TypeError 
+PASS RTCPeerConnection interface: new RTCPeerConnection() must inherit property "removeTrack" with the proper type (38) 
+PASS RTCPeerConnection interface: calling removeTrack(RTCRtpSender) on new RTCPeerConnection() with too few arguments must throw TypeError 
 FAIL RTCPeerConnection interface: new RTCPeerConnection() must inherit property "addTransceiver" with the proper type (39) assert_inherits: property "addTransceiver" not found in prototype chain
 FAIL RTCPeerConnection interface: calling addTransceiver([object Object],[object Object],RTCRtpTransceiverInit) on new RTCPeerConnection() with too few arguments must throw TypeError assert_inherits: property "addTransceiver" not found in prototype chain
 FAIL RTCPeerConnection interface: new RTCPeerConnection() must inherit property "ontrack" with the proper type (40) assert_inherits: property "ontrack" not found in prototype chain
diff --git a/third_party/WebKit/LayoutTests/fast/peerconnection/RTCPeerConnection-AddRemoveTrack.html b/third_party/WebKit/LayoutTests/fast/peerconnection/RTCPeerConnection-AddRemoveTrack.html
new file mode 100644
index 0000000..2f4d0f7
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/fast/peerconnection/RTCPeerConnection-AddRemoveTrack.html
@@ -0,0 +1,149 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>RTCPeerConnection.getSenders</title>
+<script src="../../resources/testharness.js"></script>
+<script src="../../resources/testharnessreport.js"></script>
+</head>
+<body>
+<script>
+promise_test(function() {
+  let pc = new RTCPeerConnection();
+  return createStreams({audio:true, video:false}, 1)
+    .then(function(streams) {
+      let stream = streams[0];
+      let track = stream.getAudioTracks()[0];
+      let sender = pc.addTrack(track);
+      assert_equals(sender.track, track);
+      assert_array_equals(pc.getLocalStreams(), []);
+    });
+}, 'addTrack() for a single track.');
+
+promise_test(function() {
+  let pc = new RTCPeerConnection();
+  return createStreams({audio:true, video:false}, 1)
+    .then(function(streams) {
+      let stream = streams[0];
+      let track = stream.getAudioTracks()[0];
+      let sender = pc.addTrack(track, stream);
+      assert_equals(sender.track, track);
+      // TODO(hbos): |addTrack| does not add to the set of local streams. When
+      // |getLocalStreams| is updated to return the streams of all senders this
+      // would have an observable effect here. https://crbug.com/738918
+      assert_array_equals(pc.getLocalStreams(), []);
+    });
+}, 'addTrack() for a single track and its stream.');
+
+promise_test(function() {
+  let pc = new RTCPeerConnection();
+  return createStreams({audio:true, video:false}, 2)
+    .then(function(streams) {
+      let streamA = streams[0];
+      let trackA = streamA.getAudioTracks()[0];
+      let streamB = streams[1];
+      let sender = pc.addTrack(trackA, streamB);
+      assert_equals(sender.track, trackA);
+      // TODO(hbos): |addTrack| does not add to the set of local streams. When
+      // |getLocalStreams| is updated to return the streams of all senders this
+      // would have an observable effect here. https://crbug.com/738918
+      assert_array_equals(pc.getLocalStreams(), []);
+    });
+}, 'addTrack() for a single track and a different stream.');
+
+promise_test(function() {
+  let pc = new RTCPeerConnection();
+  return createStreams({audio:true, video:false}, 2)
+    .then(function(streams) {
+      let streamA = streams[0];
+      let streamB = streams[1];
+      let track = streamA.getAudioTracks()[0];
+      let exception = null;
+      try {
+        pc.addTrack(track, streamA, streamB);
+      } catch (e) {
+        exception = e;
+      }
+      // The spec supports multiple streams per track but our implementation
+      // doesn't. Fix test when resolving https://crbug.com/webrtc/7932.
+      assert_true(exception != null);
+      assert_equals('NotSupportedError', exception.name);
+    });
+}, 'addTrack() for a single track and two streams throws NotSupportedError.');
+
+promise_test(function() {
+  let pc = new RTCPeerConnection();
+  return createStreams({audio:true, video:false}, 1)
+    .then(function(streams) {
+      let stream = streams[0];
+      let track = stream.getAudioTracks()[0];
+      let sender = pc.addTrack(track);
+      assert_equals(sender.track, track);
+      let exception = null;
+      try {
+        pc.addTrack(track);
+      } catch (e) {
+        exception = e;
+      }
+      assert_equals('InvalidAccessError', exception.name);
+    });
+}, 'addTrack() when already added throws InvalidAccessError.');
+
+promise_test(function() {
+  let pc = new RTCPeerConnection();
+  return createStreams({audio:true, video:false}, 1)
+    .then(function(streams) {
+      let stream = streams[0];
+      let track = stream.getAudioTracks()[0];
+      pc.addStream(stream);
+      let exception = null;
+      try {
+        // The interaction between |addStream| and |addTrack| is not
+        // standardized (|addStream| is non-standard). Because |addStream|
+        // creates a sender for the track and |addTrack| throws if there already
+        // exists a sender for the track.
+        pc.addTrack(track);
+      } catch (e) {
+        exception = e;
+      }
+      assert_true(exception != null);
+      assert_equals('InvalidAccessError', exception.name);
+    });
+}, 'addTrack() after addStream() throws InvalidAccessError.');
+
+promise_test(function() {
+  let pc = new RTCPeerConnection();
+  return createStreams({audio:true, video:false}, 1)
+    .then(function(streams) {
+      let stream = streams[0];
+      let track = stream.getAudioTracks()[0];
+      let sender = pc.addTrack(track);
+      assert_equals(sender.track, track);
+      assert_array_equals(pc.getSenders(), [ sender ]);
+      assert_array_equals(pc.getLocalStreams(), []);
+      pc.addStream(stream);
+      assert_array_equals(pc.getLocalStreams(), [ stream ]);
+      // The interaction between |addStream| and |addTrack| is not standardized
+      // (|addStream| is non-standard). In our implementation, the existing
+      // sender is reused by |addStream|.
+      assert_array_equals(pc.getSenders(), [ sender ]);
+    });
+}, 'addTrack() before addStream() works.');
+
+/**
+ * Helper functions.
+ */
+
+function createStreams(constraints, numStreams, streamsSoFar = []) {
+  if (numStreams == 0) {
+    return Promise.resolve(streamsSoFar);;
+  }
+  return navigator.mediaDevices.getUserMedia(constraints)
+    .then(function(stream) {
+      return createStreams(constraints,
+                           numStreams - 1,
+                           streamsSoFar.concat([stream]));
+    });
+}
+</script>
+</body>
+</html>
diff --git a/third_party/WebKit/LayoutTests/fast/peerconnection/RTCPeerConnection-getSenders.html b/third_party/WebKit/LayoutTests/fast/peerconnection/RTCPeerConnection-getSenders.html
index eb2f509..f946ca5 100644
--- a/third_party/WebKit/LayoutTests/fast/peerconnection/RTCPeerConnection-getSenders.html
+++ b/third_party/WebKit/LayoutTests/fast/peerconnection/RTCPeerConnection-getSenders.html
@@ -15,11 +15,11 @@
         pc.addStream(streams[i]);
       }
       verifyStreamAndTrackCounts(pc.getLocalStreams(), 1, true, false);
-      verifyLocalTracksHaveSenders(pc);
+      verifyLocalStreamTracksHaveSenders(pc);
       // Make sure object identities are preserved between calls.
       assert_array_equals(pc.getSenders(), pc.getSenders());
     });
-}, 'getSenders() for a single audio track.');
+}, 'getSenders() for a stream with a single audio track.');
 
 promise_test(function() {
   let pc = new RTCPeerConnection();
@@ -29,11 +29,11 @@
         pc.addStream(streams[i]);
       }
       verifyStreamAndTrackCounts(pc.getLocalStreams(), 1, false, true);
-      verifyLocalTracksHaveSenders(pc);
+      verifyLocalStreamTracksHaveSenders(pc);
       // Make sure object identities are preserved between calls.
       assert_array_equals(pc.getSenders(), pc.getSenders());
     });
-}, 'getSenders() for a single video track.');
+}, 'getSenders() for a stream with a single video track.');
 
 promise_test(function() {
   let pc = new RTCPeerConnection();
@@ -43,11 +43,11 @@
         pc.addStream(streams[i]);
       }
       verifyStreamAndTrackCounts(pc.getLocalStreams(), 1, true, true);
-      verifyLocalTracksHaveSenders(pc);
+      verifyLocalStreamTracksHaveSenders(pc);
       // Make sure object identities are preserved between calls.
       assert_array_equals(pc.getSenders(), pc.getSenders());
     });
-}, 'getSenders() for a single stream with an audio and video track.');
+}, 'getSenders() for a stream with an audio and video track.');
 
 promise_test(function() {
   let pc = new RTCPeerConnection();
@@ -57,7 +57,7 @@
         pc.addStream(streams[i]);
       }
       verifyStreamAndTrackCounts(pc.getLocalStreams(), 2, true, true);
-      verifyLocalTracksHaveSenders(pc);
+      verifyLocalStreamTracksHaveSenders(pc);
       // Make sure object identities are preserved between calls.
       assert_array_equals(pc.getSenders(), pc.getSenders());
     });
@@ -72,7 +72,7 @@
         pc.addStream(streams[i]);
       }
       verifyStreamAndTrackCounts(pc.getLocalStreams(), 2, true, true);
-      verifyLocalTracksHaveSenders(pc);
+      verifyLocalStreamTracksHaveSenders(pc);
       // Make sure object identities are preserved between calls.
       assert_array_equals(pc.getSenders(), pc.getSenders());
 
@@ -83,7 +83,7 @@
 
       pc.removeStream(pc.getLocalStreams()[0]);
       verifyStreamAndTrackCounts(pc.getLocalStreams(), 1, true, true);
-      verifyLocalTracksHaveSenders(pc);
+      verifyLocalStreamTracksHaveSenders(pc);
       // Make sure object identities are preserved between calls.
       assert_array_equals(pc.getSenders(), pc.getSenders());
 
@@ -97,7 +97,7 @@
         pc.addStream(streams[i]);
       }
       verifyStreamAndTrackCounts(pc.getLocalStreams(), 2, true, true);
-      verifyLocalTracksHaveSenders(pc);
+      verifyLocalStreamTracksHaveSenders(pc);
       // Make sure object identities are preserved between calls.
       assert_array_equals(pc.getSenders(), pc.getSenders());
 
@@ -117,6 +117,19 @@
     });
 }, 'getSenders() for streams being added and removed.');
 
+promise_test(function() {
+  let pc = new RTCPeerConnection();
+  return createStreams({audio:true, video:false}, 1)
+    .then(function(streams) {
+      let track = streams[0].getAudioTracks()[0];
+      let sender = pc.addTrack(track);
+      verifyTracksHaveSenders(pc, new Set([ track ]));
+      assert_equals(pc.getSenders()[0], sender);
+      pc.removeTrack(sender);
+      assert_equals(pc.getSenders().length, 0);
+    });
+}, 'getSenders() for addTrack() and removeTrack().');
+
 /**
  * Helper functions to tests.
  */
@@ -141,7 +154,7 @@
   }
 }
 
-function verifyLocalTracksHaveSenders(pc) {
+function verifyLocalStreamTracksHaveSenders(pc) {
   let localTracks = new Set();
   let localStreams = pc.getLocalStreams();
   for (let i = 0; i < localStreams.length; ++i) {
@@ -150,17 +163,20 @@
       localTracks.add(localStreamTracks[j]);
     }
   }
+  return verifyTracksHaveSenders(pc, localTracks);
+}
 
+function verifyTracksHaveSenders(pc, tracks) {
   let senderTracks = new Set();
   let senders = pc.getSenders();
   for (let i = 0; i < senders.length; ++i) {
     assert_true(senders[i] != null);
     assert_true(senders[i].track != null);
-    assert_true(localTracks.has(senders[i].track));
+    assert_true(tracks.has(senders[i].track));
     assert_false(senderTracks.has(senders[i].track));
     senderTracks.add(senders[i].track);
   }
-  assert_equals(senderTracks.size, localTracks.size);
+  assert_equals(senderTracks.size, tracks.size);
 }
 </script>
 </body>
diff --git a/third_party/WebKit/LayoutTests/virtual/service-worker-navigation-preload-disabled/webexposed/global-interface-listing-expected.txt b/third_party/WebKit/LayoutTests/virtual/service-worker-navigation-preload-disabled/webexposed/global-interface-listing-expected.txt
index c3fc5dc5..6fce2fc 100644
--- a/third_party/WebKit/LayoutTests/virtual/service-worker-navigation-preload-disabled/webexposed/global-interface-listing-expected.txt
+++ b/third_party/WebKit/LayoutTests/virtual/service-worker-navigation-preload-disabled/webexposed/global-interface-listing-expected.txt
@@ -5158,6 +5158,7 @@
     getter signalingState
     method addIceCandidate
     method addStream
+    method addTrack
     method close
     method constructor
     method createAnswer
@@ -5171,6 +5172,7 @@
     method getStats
     method getStreamById
     method removeStream
+    method removeTrack
     method setConfiguration
     method setLocalDescription
     method setRemoteDescription
@@ -8810,6 +8812,7 @@
     getter signalingState
     method addIceCandidate
     method addStream
+    method addTrack
     method close
     method constructor
     method createAnswer
@@ -8823,6 +8826,7 @@
     method getStats
     method getStreamById
     method removeStream
+    method removeTrack
     method setConfiguration
     method setLocalDescription
     method setRemoteDescription
diff --git a/third_party/WebKit/LayoutTests/webexposed/global-interface-listing-expected.txt b/third_party/WebKit/LayoutTests/webexposed/global-interface-listing-expected.txt
index d72d71e..befa5470 100644
--- a/third_party/WebKit/LayoutTests/webexposed/global-interface-listing-expected.txt
+++ b/third_party/WebKit/LayoutTests/webexposed/global-interface-listing-expected.txt
@@ -5165,6 +5165,7 @@
     getter signalingState
     method addIceCandidate
     method addStream
+    method addTrack
     method close
     method constructor
     method createAnswer
@@ -5178,6 +5179,7 @@
     method getStats
     method getStreamById
     method removeStream
+    method removeTrack
     method setConfiguration
     method setLocalDescription
     method setRemoteDescription
@@ -8818,6 +8820,7 @@
     getter signalingState
     method addIceCandidate
     method addStream
+    method addTrack
     method close
     method constructor
     method createAnswer
@@ -8831,6 +8834,7 @@
     method getStats
     method getStreamById
     method removeStream
+    method removeTrack
     method setConfiguration
     method setLocalDescription
     method setRemoteDescription
diff --git a/third_party/WebKit/Source/bindings/core/v8/ScriptPromiseResolverTest.cpp b/third_party/WebKit/Source/bindings/core/v8/ScriptPromiseResolverTest.cpp
index 25d9c8b1..e8fa7bd6 100644
--- a/third_party/WebKit/Source/bindings/core/v8/ScriptPromiseResolverTest.cpp
+++ b/third_party/WebKit/Source/bindings/core/v8/ScriptPromiseResolverTest.cpp
@@ -19,16 +19,16 @@
 
 namespace {
 
-class Function : public ScriptFunction {
+class TestHelperFunction : public ScriptFunction {
  public:
   static v8::Local<v8::Function> CreateFunction(ScriptState* script_state,
                                                 String* value) {
-    Function* self = new Function(script_state, value);
+    TestHelperFunction* self = new TestHelperFunction(script_state, value);
     return self->BindToV8Function();
   }
 
  private:
-  Function(ScriptState* script_state, String* value)
+  TestHelperFunction(ScriptState* script_state, String* value)
       : ScriptFunction(script_state), value_(value) {}
 
   ScriptValue Call(ScriptValue value) override {
@@ -80,8 +80,9 @@
   ASSERT_FALSE(promise.IsEmpty());
   {
     ScriptState::Scope scope(GetScriptState());
-    promise.Then(Function::CreateFunction(GetScriptState(), &on_fulfilled),
-                 Function::CreateFunction(GetScriptState(), &on_rejected));
+    promise.Then(
+        TestHelperFunction::CreateFunction(GetScriptState(), &on_fulfilled),
+        TestHelperFunction::CreateFunction(GetScriptState(), &on_rejected));
   }
 
   EXPECT_EQ(String(), on_fulfilled);
@@ -128,8 +129,9 @@
   ASSERT_FALSE(promise.IsEmpty());
   {
     ScriptState::Scope scope(GetScriptState());
-    promise.Then(Function::CreateFunction(GetScriptState(), &on_fulfilled),
-                 Function::CreateFunction(GetScriptState(), &on_rejected));
+    promise.Then(
+        TestHelperFunction::CreateFunction(GetScriptState(), &on_fulfilled),
+        TestHelperFunction::CreateFunction(GetScriptState(), &on_rejected));
   }
 
   EXPECT_EQ(String(), on_fulfilled);
@@ -176,8 +178,9 @@
   ASSERT_FALSE(promise.IsEmpty());
   {
     ScriptState::Scope scope(GetScriptState());
-    promise.Then(Function::CreateFunction(GetScriptState(), &on_fulfilled),
-                 Function::CreateFunction(GetScriptState(), &on_rejected));
+    promise.Then(
+        TestHelperFunction::CreateFunction(GetScriptState(), &on_fulfilled),
+        TestHelperFunction::CreateFunction(GetScriptState(), &on_rejected));
   }
 
   GetExecutionContext()->NotifyContextDestroyed();
@@ -316,8 +319,9 @@
   ASSERT_FALSE(promise.IsEmpty());
   {
     ScriptState::Scope scope(GetScriptState());
-    promise.Then(Function::CreateFunction(GetScriptState(), &on_fulfilled),
-                 Function::CreateFunction(GetScriptState(), &on_rejected));
+    promise.Then(
+        TestHelperFunction::CreateFunction(GetScriptState(), &on_fulfilled),
+        TestHelperFunction::CreateFunction(GetScriptState(), &on_rejected));
   }
 
   resolver->Resolve();
@@ -340,8 +344,9 @@
   ASSERT_FALSE(promise.IsEmpty());
   {
     ScriptState::Scope scope(GetScriptState());
-    promise.Then(Function::CreateFunction(GetScriptState(), &on_fulfilled),
-                 Function::CreateFunction(GetScriptState(), &on_rejected));
+    promise.Then(
+        TestHelperFunction::CreateFunction(GetScriptState(), &on_fulfilled),
+        TestHelperFunction::CreateFunction(GetScriptState(), &on_rejected));
   }
 
   resolver->Reject();
diff --git a/third_party/WebKit/Source/build/scripts/templates/CSSPropertyDescriptor.h.tmpl b/third_party/WebKit/Source/build/scripts/templates/CSSPropertyDescriptor.h.tmpl
index 8803001..6bec181a 100644
--- a/third_party/WebKit/Source/build/scripts/templates/CSSPropertyDescriptor.h.tmpl
+++ b/third_party/WebKit/Source/build/scripts/templates/CSSPropertyDescriptor.h.tmpl
@@ -2,6 +2,9 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#ifndef CSSPropertyDescriptor_h
+#define CSSPropertyDescriptor_h
+
 #include "core/CSSPropertyNames.h"
 #include "platform/heap/HeapAllocator.h"
 
@@ -26,3 +29,5 @@
 };
 
 }  // namespace blink
+
+#endif  // CSSPropertyDescriptor_h
diff --git a/third_party/WebKit/Source/build/scripts/templates/CSSValueIDMappingsGenerated.h.tmpl b/third_party/WebKit/Source/build/scripts/templates/CSSValueIDMappingsGenerated.h.tmpl
index 206d68a..2f5e376 100644
--- a/third_party/WebKit/Source/build/scripts/templates/CSSValueIDMappingsGenerated.h.tmpl
+++ b/third_party/WebKit/Source/build/scripts/templates/CSSValueIDMappingsGenerated.h.tmpl
@@ -1,6 +1,9 @@
 {% from 'macros.tmpl' import license %}
 {{license()}}
 
+#ifndef CSSValueIDMappingsGenerated_h
+#define CSSValueIDMappingsGenerated_h
+
 #include "base/logging.h"
 #include "core/CSSValueKeywords.h"
 #include "core/ComputedStyleBaseConstants.h"
@@ -51,3 +54,5 @@
 } // namespace detail
 
 } // namespace blink
+
+#endif  // CSSValueIDMappingsGenerated_h
diff --git a/third_party/WebKit/Source/core/BUILD.gn b/third_party/WebKit/Source/core/BUILD.gn
index 8e54cdb..2e8e035b 100644
--- a/third_party/WebKit/Source/core/BUILD.gn
+++ b/third_party/WebKit/Source/core/BUILD.gn
@@ -1084,6 +1084,19 @@
   foreach(current, targets_generating_sources) {
     sources += get_target_outputs(current)
   }
+  jumbo_excluded_sources = [
+    # Same variables as HTMLElementFactory
+    "$blink_core_output_dir/SVGElementFactory.cpp",
+
+    # Same variables (stringpool_t, stringpool_contents) as CSSValueKeywords
+    "$blink_core_output_dir/CSSPropertyNames.cpp",
+
+    # Global "using namespace blink" and "using namespace XPath"
+    "$blink_core_output_dir/XPathGrammar.cpp",
+
+    # Using global "using namespace WTF"
+    "//third_party/WebKit/Source/bindings/core/v8/custom/V8CSSStyleDeclarationCustom.cpp",
+  ]
   public_deps = [
     ":all_generators",
     "//third_party/WebKit/Source/bindings/core/v8:bindings_core_impl",
diff --git a/third_party/WebKit/Source/core/exported/WebPagePopupImpl.cpp b/third_party/WebKit/Source/core/exported/WebPagePopupImpl.cpp
index 53843199..fb8f1eee 100644
--- a/third_party/WebKit/Source/core/exported/WebPagePopupImpl.cpp
+++ b/third_party/WebKit/Source/core/exported/WebPagePopupImpl.cpp
@@ -321,8 +321,7 @@
   frame->SetPageZoomFactor(popup_client_->ZoomFactor());
   frame->Loader().Load(
       FrameLoadRequest(0, ResourceRequest(BlankURL()),
-                       SubstituteData(data, "text/html", "UTF-8", NullURL(),
-                                      kForceSynchronousLoad)));
+                       SubstituteData(data, kForceSynchronousLoad)));
   return true;
 }
 
diff --git a/third_party/WebKit/Source/core/frame/FrameTest.cpp b/third_party/WebKit/Source/core/frame/FrameTest.cpp
index d221c70..a77b7bf 100644
--- a/third_party/WebKit/Source/core/frame/FrameTest.cpp
+++ b/third_party/WebKit/Source/core/frame/FrameTest.cpp
@@ -27,8 +27,7 @@
   void Navigate(const String& destinationUrl) {
     const KURL& url = KURL(NullURL(), destinationUrl);
     FrameLoadRequest request(nullptr, ResourceRequest(url),
-                             SubstituteData(SharedBuffer::Create(), "text/html",
-                                            "UTF-8", NullURL()));
+                             SubstituteData(SharedBuffer::Create()));
     GetDocument().GetFrame()->Loader().Load(request);
     blink::testing::RunPendingTasks();
     ASSERT_EQ(url.GetString(), GetDocument().Url().GetString());
diff --git a/third_party/WebKit/Source/core/inspector/InspectorOverlayAgent.cpp b/third_party/WebKit/Source/core/inspector/InspectorOverlayAgent.cpp
index efe726a..79c068c 100644
--- a/third_party/WebKit/Source/core/inspector/InspectorOverlayAgent.cpp
+++ b/third_party/WebKit/Source/core/inspector/InspectorOverlayAgent.cpp
@@ -844,8 +844,7 @@
       Platform::Current()->GetDataResource("InspectorOverlayPage.html");
   loader.Load(FrameLoadRequest(
       0, ResourceRequest(BlankURL()),
-      SubstituteData(overlay_page_html_resource, "text/html", "UTF-8",
-                     NullURL(), kForceSynchronousLoad)));
+      SubstituteData(overlay_page_html_resource, kForceSynchronousLoad)));
   v8::Isolate* isolate = ToIsolate(frame);
   ScriptState* script_state = ToScriptStateForMainWorld(frame);
   DCHECK(script_state);
diff --git a/third_party/WebKit/Source/core/loader/PingLoaderTest.cpp b/third_party/WebKit/Source/core/loader/PingLoaderTest.cpp
index a6e72f6..ced63815 100644
--- a/third_party/WebKit/Source/core/loader/PingLoaderTest.cpp
+++ b/third_party/WebKit/Source/core/loader/PingLoaderTest.cpp
@@ -48,8 +48,7 @@
 
   void SetDocumentURL(const KURL& url) {
     FrameLoadRequest request(nullptr, ResourceRequest(url),
-                             SubstituteData(SharedBuffer::Create(), "text/html",
-                                            "UTF-8", NullURL()));
+                             SubstituteData(SharedBuffer::Create()));
     page_holder_->GetFrame().Loader().Load(request);
     blink::testing::RunPendingTasks();
     ASSERT_EQ(url.GetString(), page_holder_->GetDocument().Url().GetString());
diff --git a/third_party/WebKit/Source/modules/exported/WebEmbeddedWorkerImpl.cpp b/third_party/WebKit/Source/modules/exported/WebEmbeddedWorkerImpl.cpp
index 1e29852..7b03a17 100644
--- a/third_party/WebKit/Source/modules/exported/WebEmbeddedWorkerImpl.cpp
+++ b/third_party/WebKit/Source/modules/exported/WebEmbeddedWorkerImpl.cpp
@@ -318,9 +318,9 @@
   RefPtr<SharedBuffer> buffer(
       SharedBuffer::Create(content.data(), content.length()));
   loading_shadow_page_ = true;
-  main_frame_->GetFrame()->Loader().Load(FrameLoadRequest(
-      0, ResourceRequest(worker_start_data_.script_url),
-      SubstituteData(buffer, "text/html", "UTF-8", NullURL())));
+  main_frame_->GetFrame()->Loader().Load(
+      FrameLoadRequest(0, ResourceRequest(worker_start_data_.script_url),
+                       SubstituteData(buffer)));
 }
 
 void WebEmbeddedWorkerImpl::FrameDetached(WebLocalFrame* frame,
diff --git a/third_party/WebKit/Source/modules/peerconnection/RTCPeerConnection.cpp b/third_party/WebKit/Source/modules/peerconnection/RTCPeerConnection.cpp
index e4d4603..63866d30 100644
--- a/third_party/WebKit/Source/modules/peerconnection/RTCPeerConnection.cpp
+++ b/third_party/WebKit/Source/modules/peerconnection/RTCPeerConnection.cpp
@@ -1110,6 +1110,8 @@
   if (!valid)
     exception_state.ThrowDOMException(kSyntaxError,
                                       "Unable to add the provided stream.");
+  // Ensure |rtp_senders_| is up-to-date.
+  getSenders();
 }
 
 void RTCPeerConnection::removeStream(MediaStream* stream,
@@ -1236,6 +1238,75 @@
   return rtp_receivers;
 }
 
+RTCRtpSender* RTCPeerConnection::addTrack(MediaStreamTrack* track,
+                                          MediaStreamVector streams,
+                                          ExceptionState& exception_state) {
+  DCHECK(track);
+  DCHECK(track->Component());
+  if (ThrowExceptionIfSignalingStateClosed(signaling_state_, exception_state))
+    return nullptr;
+  if (streams.size() >= 2) {
+    // TODO(hbos): Don't throw an exception when this is supported by the lower
+    // layers. https://crbug.com/webrtc/7932
+    exception_state.ThrowDOMException(
+        kNotSupportedError,
+        "Adding a track to multiple streams is not supported.");
+    return nullptr;
+  }
+  for (const auto sender_entry : rtp_senders_) {
+    RTCRtpSender* sender = sender_entry.value;
+    if (sender->track() == track) {
+      exception_state.ThrowDOMException(
+          kInvalidAccessError, "A sender already exists for the track.");
+      return nullptr;
+    }
+  }
+
+  WebVector<WebMediaStream> web_streams(streams.size());
+  for (size_t i = 0; i < streams.size(); ++i) {
+    web_streams[i] = streams[i]->Descriptor();
+  }
+  std::unique_ptr<WebRTCRtpSender> web_rtp_sender =
+      peer_handler_->AddTrack(track->Component(), web_streams);
+  if (!web_rtp_sender) {
+    exception_state.ThrowDOMException(
+        kNotSupportedError, "A sender could not be created for this track.");
+    return nullptr;
+  }
+
+  uintptr_t id = web_rtp_sender->Id();
+  DCHECK(rtp_senders_.find(id) == rtp_senders_.end());
+  RTCRtpSender* rtp_sender = new RTCRtpSender(std::move(web_rtp_sender), track);
+  tracks_.insert(track->Component(), track);
+  rtp_senders_.insert(id, rtp_sender);
+  return rtp_sender;
+}
+
+void RTCPeerConnection::removeTrack(RTCRtpSender* sender,
+                                    ExceptionState& exception_state) {
+  DCHECK(sender);
+  if (ThrowExceptionIfSignalingStateClosed(signaling_state_, exception_state))
+    return;
+  if (rtp_senders_.find(sender->web_rtp_sender()->Id()) == rtp_senders_.end()) {
+    exception_state.ThrowDOMException(
+        kInvalidAccessError,
+        "The sender was not created by this peer connection.");
+  }
+
+  if (!peer_handler_->RemoveTrack(sender->web_rtp_sender())) {
+    // Operation aborted. This indicates that the sender is no longer used by
+    // the peer connection, i.e. that it was removed due to setting a remote
+    // description of type "rollback".
+    // Note: Until the WebRTC library supports re-using senders, a sender will
+    // also stop being used as a result of being removed.
+    return;
+  }
+  // Successfully removing the track results in the sender's track property
+  // being nulled.
+  DCHECK(!sender->web_rtp_sender()->Track());
+  sender->SetTrack(nullptr);
+}
+
 RTCDataChannel* RTCPeerConnection::createDataChannel(
     ScriptState* script_state,
     String label,
diff --git a/third_party/WebKit/Source/modules/peerconnection/RTCPeerConnection.h b/third_party/WebKit/Source/modules/peerconnection/RTCPeerConnection.h
index d9fba38..cc5be6f8 100644
--- a/third_party/WebKit/Source/modules/peerconnection/RTCPeerConnection.h
+++ b/third_party/WebKit/Source/modules/peerconnection/RTCPeerConnection.h
@@ -156,6 +156,8 @@
 
   HeapVector<Member<RTCRtpSender>> getSenders();
   HeapVector<Member<RTCRtpReceiver>> getReceivers();
+  RTCRtpSender* addTrack(MediaStreamTrack*, MediaStreamVector, ExceptionState&);
+  void removeTrack(RTCRtpSender*, ExceptionState&);
 
   RTCDataChannel* createDataChannel(ScriptState*,
                                     String label,
diff --git a/third_party/WebKit/Source/modules/peerconnection/RTCPeerConnection.idl b/third_party/WebKit/Source/modules/peerconnection/RTCPeerConnection.idl
index 91e8a8d..1c5c9117a5 100644
--- a/third_party/WebKit/Source/modules/peerconnection/RTCPeerConnection.idl
+++ b/third_party/WebKit/Source/modules/peerconnection/RTCPeerConnection.idl
@@ -112,10 +112,15 @@
     // spec, remove it or change it?): https://github.com/w3c/webrtc-stats/issues/116
     [CallWith=ScriptState] Promise<RTCStatsReport> getStats();
 
+    // RTP Media API
     // https://w3c.github.io/webrtc-pc/#dom-rtcpeerconnection-getsenders
     [RuntimeEnabled=RTCRtpSender] sequence<RTCRtpSender> getSenders();
     // https://w3c.github.io/webrtc-pc/#dom-rtcpeerconnection-getreceivers
     sequence<RTCRtpReceiver> getReceivers();
+    // https://w3c.github.io/webrtc-pc/#dom-rtcpeerconnection-addtrack
+    [RuntimeEnabled=RTCRtpSender, RaisesException] RTCRtpSender addTrack(MediaStreamTrack track, MediaStream... streams);
+    // https://w3c.github.io/webrtc-pc/#dom-rtcpeerconnection-removetrack
+    [RuntimeEnabled=RTCRtpSender, RaisesException] void removeTrack(RTCRtpSender sender);
 
     // https://w3c.github.io/webrtc-pc/#peer-to-peer-data-api
     [CallWith=ScriptState, RaisesException] RTCDataChannel createDataChannel(USVString label, optional RTCDataChannelInit dataChannelDict);
diff --git a/third_party/WebKit/Source/modules/peerconnection/RTCRtpSender.cpp b/third_party/WebKit/Source/modules/peerconnection/RTCRtpSender.cpp
index c31fd2c7..b6e83853 100644
--- a/third_party/WebKit/Source/modules/peerconnection/RTCRtpSender.cpp
+++ b/third_party/WebKit/Source/modules/peerconnection/RTCRtpSender.cpp
@@ -8,6 +8,17 @@
 
 namespace blink {
 
+namespace {
+
+bool TrackEquals(const MediaStreamTrack* track,
+                 const WebMediaStreamTrack* web_track) {
+  if (track)
+    return web_track && web_track->Id() == WebString(track->id());
+  return !web_track;
+}
+
+}  // namespace
+
 RTCRtpSender::RTCRtpSender(std::unique_ptr<WebRTCRtpSender> sender,
                            MediaStreamTrack* track)
     : sender_(std::move(sender)), track_(track) {
@@ -16,12 +27,19 @@
 }
 
 MediaStreamTrack* RTCRtpSender::track() {
-  DCHECK((track_ && sender_->Track() &&
-          sender_->Track()->Id() == static_cast<WebString>(track_->id())) ||
-         (!track_ && !sender_->Track()));
+  DCHECK(TrackEquals(track_, sender_->Track()));
   return track_;
 }
 
+WebRTCRtpSender* RTCRtpSender::web_rtp_sender() {
+  return sender_.get();
+}
+
+void RTCRtpSender::SetTrack(MediaStreamTrack* track) {
+  DCHECK(TrackEquals(track, sender_->Track()));
+  track_ = track;
+}
+
 DEFINE_TRACE(RTCRtpSender) {
   visitor->Trace(track_);
 }
diff --git a/third_party/WebKit/Source/modules/peerconnection/RTCRtpSender.h b/third_party/WebKit/Source/modules/peerconnection/RTCRtpSender.h
index ce79a2c..576c2127 100644
--- a/third_party/WebKit/Source/modules/peerconnection/RTCRtpSender.h
+++ b/third_party/WebKit/Source/modules/peerconnection/RTCRtpSender.h
@@ -26,6 +26,11 @@
 
   MediaStreamTrack* track();
 
+  WebRTCRtpSender* web_rtp_sender();
+  // Sets the track. This must be called when the |WebRTCRtpSender| has its
+  // track updated, and the |track| must match the |WebRTCRtpSender::Track|.
+  void SetTrack(MediaStreamTrack*);
+
   DECLARE_VIRTUAL_TRACE();
 
  private:
diff --git a/third_party/WebKit/Source/modules/sensor/Sensor.cpp b/third_party/WebKit/Source/modules/sensor/Sensor.cpp
index 69305dba..174e78a9 100644
--- a/third_party/WebKit/Source/modules/sensor/Sensor.cpp
+++ b/third_party/WebKit/Source/modules/sensor/Sensor.cpp
@@ -14,8 +14,6 @@
 #include "modules/sensor/SensorProviderProxy.h"
 #include "services/device/public/interfaces/sensor.mojom-blink.h"
 
-using namespace device::mojom::blink;
-
 namespace blink {
 
 namespace {
@@ -28,7 +26,7 @@
 Sensor::Sensor(ExecutionContext* execution_context,
                const SensorOptions& sensor_options,
                ExceptionState& exception_state,
-               SensorType type)
+               device::mojom::blink::SensorType type)
     : ContextLifecycleObserver(execution_context),
       sensor_options_(sensor_options),
       type_(type),
diff --git a/third_party/WebKit/Source/modules/sensor/SensorProxy.cpp b/third_party/WebKit/Source/modules/sensor/SensorProxy.cpp
index 44a4eb2..c1ddf208 100644
--- a/third_party/WebKit/Source/modules/sensor/SensorProxy.cpp
+++ b/third_party/WebKit/Source/modules/sensor/SensorProxy.cpp
@@ -11,10 +11,10 @@
 #include "platform/mojo/MojoHelper.h"
 #include "public/platform/Platform.h"
 
-using namespace device::mojom::blink;
-
 namespace blink {
 
+using namespace device::mojom::blink;
+
 SensorProxy::SensorProxy(SensorType sensor_type,
                          SensorProviderProxy* provider,
                          Page* page)
diff --git a/third_party/WebKit/Source/platform/loader/fetch/SubstituteData.h b/third_party/WebKit/Source/platform/loader/fetch/SubstituteData.h
index af424d28..67cd49d 100644
--- a/third_party/WebKit/Source/platform/loader/fetch/SubstituteData.h
+++ b/third_party/WebKit/Source/platform/loader/fetch/SubstituteData.h
@@ -43,6 +43,15 @@
 
   SubstituteData(
       RefPtr<SharedBuffer> content,
+      SubstituteDataLoadPolicy substitute_data_load_policy = kLoadNormally)
+      : SubstituteData(content,
+                       "text/html",
+                       "UTF-8",
+                       KURL(),
+                       substitute_data_load_policy) {}
+
+  SubstituteData(
+      RefPtr<SharedBuffer> content,
       const AtomicString& mime_type,
       const AtomicString& text_encoding,
       const KURL& failing_url,
diff --git a/third_party/WebKit/Source/platform/testing/TestingPlatformSupportWithWebRTC.cpp b/third_party/WebKit/Source/platform/testing/TestingPlatformSupportWithWebRTC.cpp
index ab9338d..3cd3d3a7 100644
--- a/third_party/WebKit/Source/platform/testing/TestingPlatformSupportWithWebRTC.cpp
+++ b/third_party/WebKit/Source/platform/testing/TestingPlatformSupportWithWebRTC.cpp
@@ -80,6 +80,16 @@
   return WebVector<std::unique_ptr<WebRTCRtpReceiver>>();
 }
 
+std::unique_ptr<WebRTCRtpSender> MockWebRTCPeerConnectionHandler::AddTrack(
+    const WebMediaStreamTrack&,
+    const WebVector<WebMediaStream>&) {
+  return nullptr;
+}
+
+bool MockWebRTCPeerConnectionHandler::RemoveTrack(WebRTCRtpSender*) {
+  return false;
+}
+
 WebRTCDataChannelHandler* MockWebRTCPeerConnectionHandler::CreateDataChannel(
     const WebString& label,
     const WebRTCDataChannelInit&) {
diff --git a/third_party/WebKit/Source/platform/testing/TestingPlatformSupportWithWebRTC.h b/third_party/WebKit/Source/platform/testing/TestingPlatformSupportWithWebRTC.h
index e5c5e2b..06b4ffa4 100644
--- a/third_party/WebKit/Source/platform/testing/TestingPlatformSupportWithWebRTC.h
+++ b/third_party/WebKit/Source/platform/testing/TestingPlatformSupportWithWebRTC.h
@@ -39,6 +39,10 @@
   void GetStats(std::unique_ptr<WebRTCStatsReportCallback>) override;
   WebVector<std::unique_ptr<WebRTCRtpSender>> GetSenders() override;
   WebVector<std::unique_ptr<WebRTCRtpReceiver>> GetReceivers() override;
+  std::unique_ptr<WebRTCRtpSender> AddTrack(
+      const WebMediaStreamTrack&,
+      const WebVector<WebMediaStream>&) override;
+  bool RemoveTrack(WebRTCRtpSender*) override;
   WebRTCDataChannelHandler* CreateDataChannel(
       const WebString& label,
       const WebRTCDataChannelInit&) override;
diff --git a/third_party/WebKit/public/BUILD.gn b/third_party/WebKit/public/BUILD.gn
index 6b9fb39f..8f65863 100644
--- a/third_party/WebKit/public/BUILD.gn
+++ b/third_party/WebKit/public/BUILD.gn
@@ -724,6 +724,7 @@
     "platform/modules/bluetooth/web_bluetooth.mojom",
     "platform/modules/broadcastchannel/broadcast_channel.mojom",
     "platform/modules/budget_service/budget_service.mojom",
+    "platform/modules/credentialmanager/credential_manager.mojom",
     "platform/modules/fetch/fetch_api_request.mojom",
     "platform/modules/hyphenation/hyphenation.mojom",
     "platform/modules/keyboard_lock/keyboard_lock.mojom",
diff --git a/third_party/WebKit/public/platform/WebRTCPeerConnectionHandler.h b/third_party/WebKit/public/platform/WebRTCPeerConnectionHandler.h
index eefeb61..70b273078 100644
--- a/third_party/WebKit/public/platform/WebRTCPeerConnectionHandler.h
+++ b/third_party/WebKit/public/platform/WebRTCPeerConnectionHandler.h
@@ -103,6 +103,14 @@
   // webrtc-layer receivers, multiple |WebRTCRtpReceiver| objects referencing
   // the same webrtc-layer receiver have the same |id|.
   virtual WebVector<std::unique_ptr<WebRTCRtpReceiver>> GetReceivers() = 0;
+  // Adds the track to the peer connection, returning the resulting sender on
+  // success and null on failure.
+  virtual std::unique_ptr<WebRTCRtpSender> AddTrack(
+      const WebMediaStreamTrack&,
+      const WebVector<WebMediaStream>&) = 0;
+  // Removes the sender, returning whether successful. On success, the sender's
+  // track must have been set to null.
+  virtual bool RemoveTrack(WebRTCRtpSender*) = 0;
   virtual WebRTCDTMFSenderHandler* CreateDTMFSender(
       const WebMediaStreamTrack&) = 0;
   virtual void Stop() = 0;
diff --git a/third_party/WebKit/public/platform/modules/credentialmanager/OWNERS b/third_party/WebKit/public/platform/modules/credentialmanager/OWNERS
new file mode 100644
index 0000000..cae285e
--- /dev/null
+++ b/third_party/WebKit/public/platform/modules/credentialmanager/OWNERS
@@ -0,0 +1,9 @@
+engedy@chromium.org
+mkwst@chromium.org
+vasilii@chromium.org
+
+per-file *.mojom=set noparent
+per-file *.mojom=file://ipc/SECURITY_OWNERS
+
+# COMPONENT: Blink>SecurityFeature>CredentialManagement
+# TEAM: identity-dev@chromium.org
diff --git a/components/password_manager/content/common/credential_manager.mojom b/third_party/WebKit/public/platform/modules/credentialmanager/credential_manager.mojom
similarity index 100%
rename from components/password_manager/content/common/credential_manager.mojom
rename to third_party/WebKit/public/platform/modules/credentialmanager/credential_manager.mojom
diff --git a/third_party/material_design_icons/BUILD.gn b/third_party/material_design_icons/BUILD.gn
index ad6fa40..6a3af431 100644
--- a/third_party/material_design_icons/BUILD.gn
+++ b/third_party/material_design_icons/BUILD.gn
@@ -20,22 +20,42 @@
   "action/ic_print",
   "action/ic_report_problem",
   "action/ic_search",
+  "action/ic_settings_white",
   "communication/ic_comment",
   "communication/ic_email",
   "communication/ic_message",
   "content/ic_link",
   "editor/ic_mode_edit",
   "file/ic_file_download",
+  "hardware/ic_desktop_windows",
+  "hardware/ic_desktop_windows_white",
   "hardware/ic_keyboard_arrow_right",
+  "hardware/ic_mouse",
   "image/ic_photo_camera",
   "image/ic_photo_library",
   "navigation/ic_arrow_back",
+  "navigation/ic_arrow_forward_white",
   "navigation/ic_check",
   "navigation/ic_chevron_left",
+  "navigation/ic_chevron_left_white_36pt",
   "navigation/ic_chevron_right",
   "navigation/ic_close",
+  "navigation/ic_close_white",
+  "navigation/ic_fullscreen",
+  "navigation/ic_fullscreen_exit",
+  "navigation/ic_menu_white",
   "navigation/ic_more_vert",
   "navigation/ic_refresh",
+  "navigation/ic_refresh_white",
+  "toggle/ic_check_box_outline_blank_white",
+  "toggle/ic_check_box_white",
+  "toggle/ic_radio_button_checked_white",
+  "toggle/ic_radio_button_unchecked_white",
+
+  # Note: this imageset is incorrect (miss the Contents.json file) but it
+  # appears to be unused, so for the moment keep it commented out. See
+  # https://github.com/google/material-design-icons/issues/630 for progress.
+  # "action/ic_touch_app",
 ]
 
 # Define all the imagesets using the description from _image_sets variable.
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml
index b29ad08..ccb12be 100644
--- a/tools/metrics/histograms/histograms.xml
+++ b/tools/metrics/histograms/histograms.xml
@@ -71373,6 +71373,14 @@
   </summary>
 </histogram>
 
+<histogram name="Signin.AndroidGmsUpdatingDialogShownTime" units="ms">
+  <owner>bsazonov@chromium.org</owner>
+  <summary>
+    How long the &quot;Waiting for Google Play Services to finish updating&quot;
+    dialog is shown.
+  </summary>
+</histogram>
+
 <histogram name="Signin.AndroidSigninPromoAction"
     enum="AndroidSigninPromoAction">
   <obsolete>
diff --git a/tools/perf/page_sets/system_health/expectations.py b/tools/perf/page_sets/system_health/expectations.py
index 3a4248d..fafb61796 100644
--- a/tools/perf/page_sets/system_health/expectations.py
+++ b/tools/perf/page_sets/system_health/expectations.py
@@ -24,8 +24,6 @@
     self.DisableStory('browse:tech:discourse_infinite_scroll',
                       [expectations.ALL_WIN, expectations.ALL_LINUX],
                       'crbug.com/728152')
-    self.DisableStory('browse:social:facebook_infinite_scroll',
-                      [expectations.ALL_WIN], 'crbug.com/728152')
     self.DisableStory('browse:news:cnn',
                       [expectations.ALL_MAC], 'crbug.com/728576')
 
@@ -52,8 +50,6 @@
     self.DisableStory('browse:tech:discourse_infinite_scroll',
                       [expectations.ALL_WIN, expectations.ALL_LINUX],
                       'crbug.com/728152')
-    self.DisableStory('browse:social:facebook_infinite_scroll',
-                      [expectations.ALL_WIN], 'crbug.com/728152')
     self.DisableStory('browse:news:cnn',
                       [expectations.ALL_MAC], 'crbug.com/728576')
     self.DisableStory('browse:social:twitter_infinite_scroll',
@@ -76,8 +72,6 @@
                       [expectations.ALL_ANDROID], 'crbug.com/726301')
     self.DisableStory('browse:news:toi', [expectations.ALL_ANDROID],
                       'crbug.com/728081')
-    self.DisableStory('browse:social:facebook_infinite_scroll',
-                      [expectations.ALL], 'crbug.com/728152')
     self.DisableStory(
         'load:tools:drive',
         [expectations.ANDROID_NEXUS5X, expectations.ANDROID_WEBVIEW],
@@ -114,8 +108,6 @@
                       [expectations.ALL_ANDROID], 'crbug.com/726301')
     self.DisableStory('browse:news:toi', [expectations.ALL_ANDROID],
                       'crbug.com/728081')
-    self.DisableStory('browse:social:facebook_infinite_scroll',
-                      [expectations.ALL], 'crbug.com/728152')
     self.DisableStory(
         'load:tools:drive',
         [expectations.ANDROID_NEXUS5X, expectations.ANDROID_WEBVIEW],
@@ -165,8 +157,6 @@
                       'crbug.com/714650')
     self.DisableStory('browse:news:toi', [expectations.ALL_ANDROID],
                       'crbug.com/728081')
-    self.DisableStory('browse:social:facebook_infinite_scroll',
-                      [expectations.ALL], 'crbug.com/728152')
     # TODO(rnephew): This disabling should move to CanRunOnBrowser.
     self.DisableStory('browse:chrome:omnibox',
                       [expectations.ANDROID_WEBVIEW],
diff --git a/ui/ozone/platform/drm/gpu/drm_device.cc b/ui/ozone/platform/drm/gpu/drm_device.cc
index 81098fa..ade4e88 100644
--- a/ui/ozone/platform/drm/gpu/drm_device.cc
+++ b/ui/ozone/platform/drm/gpu/drm_device.cc
@@ -25,7 +25,7 @@
 #include "ui/ozone/platform/drm/common/drm_util.h"
 #include "ui/ozone/platform/drm/gpu/hardware_display_plane_manager_atomic.h"
 #include "ui/ozone/platform/drm/gpu/hardware_display_plane_manager_legacy.h"
-
+#include "ui/ozone/public/ozone_switches.h"
 
 namespace ui {
 
@@ -409,15 +409,18 @@
     return false;
   }
 
-  // Use atomic only if the build, kernel & flags all allow it.
-  if (use_atomic && SetCapability(DRM_CLIENT_CAP_ATOMIC, 1))
+  if (use_atomic) {
+    if (!SetCapability(DRM_CLIENT_CAP_ATOMIC, 1)) {
+      LOG(ERROR) << "Drm atomic requested but capabilities don't allow it. To "
+                    "switch to legacy page flip remove the command line flag "
+                 << switches::kEnableDrmAtomic;
+      return false;
+    }
     plane_manager_.reset(new HardwareDisplayPlaneManagerAtomic());
-
-  LOG_IF(WARNING, use_atomic && !plane_manager_)
-      << "Drm atomic requested but capabilities don't allow it. Falling back "
-         "to legacy page flip.";
-  if (!plane_manager_)
+  } else {
     plane_manager_.reset(new HardwareDisplayPlaneManagerLegacy());
+  }
+
   if (!plane_manager_->Initialize(this)) {
     LOG(ERROR) << "Failed to initialize the plane manager for "
                << device_path_.value();
diff --git a/ui/views/mus/aura_init.cc b/ui/views/mus/aura_init.cc
index f53872d..762472b 100644
--- a/ui/views/mus/aura_init.cc
+++ b/ui/views/mus/aura_init.cc
@@ -52,40 +52,9 @@
 
 }  // namespace
 
-AuraInit::AuraInit(service_manager::Connector* connector,
-                   const service_manager::Identity& identity,
-                   const std::string& resource_file,
-                   const std::string& resource_file_200,
-                   scoped_refptr<base::SingleThreadTaskRunner> io_task_runner,
-                   Mode mode)
-    : resource_file_(resource_file),
-      resource_file_200_(resource_file_200),
-      env_(aura::Env::CreateInstance(
-          (mode == Mode::AURA_MUS || mode == Mode::AURA_MUS_WINDOW_MANAGER)
-              ? aura::Env::Mode::MUS
-              : aura::Env::Mode::LOCAL)) {
+AuraInit::AuraInit() {
   if (!ViewsDelegate::GetInstance())
     views_delegate_ = base::MakeUnique<MusViewsDelegate>();
-  if (mode == Mode::AURA_MUS) {
-    mus_client_ =
-        base::WrapUnique(new MusClient(connector, identity, io_task_runner));
-  }
-  ui::MaterialDesignController::Initialize();
-  if (!InitializeResources(connector))
-    return;
-
-// Initialize the skia font code to go ask fontconfig underneath.
-#if defined(OS_LINUX)
-  font_loader_ = sk_make_sp<font_service::FontLoader>(connector);
-  SkFontConfigInterface::SetGlobal(font_loader_.get());
-#endif
-
-  // There is a bunch of static state in gfx::Font, by running this now,
-  // before any other apps load, we ensure all the state is set up.
-  gfx::Font();
-
-  ui::InitializeInputMethodForTesting();
-  initialized_ = true;
 }
 
 AuraInit::~AuraInit() {
@@ -100,15 +69,65 @@
 #endif
 }
 
-bool AuraInit::InitializeResources(service_manager::Connector* connector) {
+std::unique_ptr<AuraInit> AuraInit::Create(
+    service_manager::Connector* connector,
+    const service_manager::Identity& identity,
+    const std::string& resource_file,
+    const std::string& resource_file_200,
+    scoped_refptr<base::SingleThreadTaskRunner> io_task_runner,
+    Mode mode) {
+  std::unique_ptr<AuraInit> aura_init = base::WrapUnique(new AuraInit());
+  if (!aura_init->Init(connector, identity, resource_file, resource_file_200,
+                       io_task_runner, mode)) {
+    aura_init.reset();
+  }
+  return aura_init;
+}
+
+bool AuraInit::Init(service_manager::Connector* connector,
+                    const service_manager::Identity& identity,
+                    const std::string& resource_file,
+                    const std::string& resource_file_200,
+                    scoped_refptr<base::SingleThreadTaskRunner> io_task_runner,
+                    Mode mode) {
+  env_ = aura::Env::CreateInstance(
+      (mode == Mode::AURA_MUS || mode == Mode::AURA_MUS_WINDOW_MANAGER)
+          ? aura::Env::Mode::MUS
+          : aura::Env::Mode::LOCAL);
+
+  if (mode == Mode::AURA_MUS) {
+    mus_client_ =
+        base::WrapUnique(new MusClient(connector, identity, io_task_runner));
+  }
+  ui::MaterialDesignController::Initialize();
+  if (!InitializeResources(connector, resource_file, resource_file_200))
+    return false;
+
+// Initialize the skia font code to go ask fontconfig underneath.
+#if defined(OS_LINUX)
+  font_loader_ = sk_make_sp<font_service::FontLoader>(connector);
+  SkFontConfigInterface::SetGlobal(font_loader_.get());
+#endif
+
+  // There is a bunch of static state in gfx::Font, by running this now,
+  // before any other apps load, we ensure all the state is set up.
+  gfx::Font();
+
+  ui::InitializeInputMethodForTesting();
+  return true;
+}
+
+bool AuraInit::InitializeResources(service_manager::Connector* connector,
+                                   const std::string& resource_file,
+                                   const std::string& resource_file_200) {
   // Resources may have already been initialized (e.g. when 'chrome --mash' is
   // used to launch the current app).
   if (ui::ResourceBundle::HasSharedInstance())
-    return false;
+    return true;
 
-  std::set<std::string> resource_paths({resource_file_});
-  if (!resource_file_200_.empty())
-    resource_paths.insert(resource_file_200_);
+  std::set<std::string> resource_paths({resource_file});
+  if (!resource_file_200.empty())
+    resource_paths.insert(resource_file_200);
 
   catalog::ResourceLoader loader;
   filesystem::mojom::DirectoryPtr directory;
@@ -123,15 +142,15 @@
   if (!loader.OpenFiles(std::move(directory), resource_paths))
     return false;
   ui::RegisterPathProvider();
-  base::File pak_file = loader.TakeFile(resource_file_);
+  base::File pak_file = loader.TakeFile(resource_file);
   base::File pak_file_2 = pak_file.Duplicate();
   ui::ResourceBundle::InitSharedInstanceWithPakFileRegion(
       std::move(pak_file), base::MemoryMappedFile::Region::kWholeFile);
   ui::ResourceBundle::GetSharedInstance().AddDataPackFromFile(
       std::move(pak_file_2), ui::SCALE_FACTOR_100P);
-  if (!resource_file_200_.empty())
+  if (!resource_file_200.empty())
     ui::ResourceBundle::GetSharedInstance().AddDataPackFromFile(
-        loader.TakeFile(resource_file_200_), ui::SCALE_FACTOR_200P);
+        loader.TakeFile(resource_file_200), ui::SCALE_FACTOR_200P);
   return true;
 }
 
diff --git a/ui/views/mus/aura_init.h b/ui/views/mus/aura_init.h
index 15deb095..2ba6fd2 100644
--- a/ui/views/mus/aura_init.h
+++ b/ui/views/mus/aura_init.h
@@ -51,42 +51,51 @@
     UI
   };
 
+  ~AuraInit();
+
+  // Returns an AuraInit if initialization can be completed successfully,
+  // otherwise a nullptr is returned. If initialization fails then Aura is in an
+  // unusable state, and calling services should shutdown.
   // |resource_file| is the file to load strings and 1x icons from.
   // |resource_file_200| can be an empty string, otherwise it is the file to
   // load 2x icons from.
-  AuraInit(service_manager::Connector* connector,
-           const service_manager::Identity& identity,
-           const std::string& resource_file,
-           const std::string& resource_file_200 = std::string(),
-           scoped_refptr<base::SingleThreadTaskRunner> io_task_runner = nullptr,
-           Mode mode = Mode::UI);
-  ~AuraInit();
+  static std::unique_ptr<AuraInit> Create(
+      service_manager::Connector* connector,
+      const service_manager::Identity& identity,
+      const std::string& resource_file,
+      const std::string& resource_file_200 = std::string(),
+      scoped_refptr<base::SingleThreadTaskRunner> io_task_runner = nullptr,
+      Mode mode = Mode::UI);
 
   // Only valid if Mode::AURA_MUS was passed to constructor.
   MusClient* mus_client() { return mus_client_.get(); }
 
+ private:
+  AuraInit();
+
   // Returns true if AuraInit was able to successfully complete initialization.
   // If this returns false, then Aura is in an unusable state, and calling
   // services should shutdown.
-  bool initialized() { return initialized_; }
+  bool Init(
+      service_manager::Connector* connector,
+      const service_manager::Identity& identity,
+      const std::string& resource_file,
+      const std::string& resource_file_200 = std::string(),
+      scoped_refptr<base::SingleThreadTaskRunner> io_task_runner = nullptr,
+      Mode mode = Mode::UI);
 
- private:
-  bool InitializeResources(service_manager::Connector* connector);
+  bool InitializeResources(service_manager::Connector* connector,
+                           const std::string& resource_file,
+                           const std::string& resource_file_200);
 
 #if defined(OS_LINUX)
   sk_sp<font_service::FontLoader> font_loader_;
 #endif
 
-  const std::string resource_file_;
-  const std::string resource_file_200_;
-
   std::unique_ptr<aura::Env> env_;
   std::unique_ptr<MusClient> mus_client_;
   std::unique_ptr<ViewsDelegate> views_delegate_;
 
-  // Whether or not initialization succeeds.
-  bool initialized_ = false;
-
   DISALLOW_COPY_AND_ASSIGN(AuraInit);
 };