diff --git a/DEPS b/DEPS
index 369de67..fee2358 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': '2644e367904984e9cced64eaf3b911a0acb231d8',
+  'v8_revision': '5ba6e0d1657de49d441efa7d599b7cb58c2c568e',
   # 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': 'c7739322e5cdcc5779bdde2a1560ea3dee891e51',
+  'pdfium_revision': '37b12ad873198a9644f3de0d2eff001285e1ad42',
   # 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': 'ab7345371ad03603c5434f2a775ec65830067920',
+  'catapult_revision': 'f87ce97ea1faeb9e553108218910551fd6492d2f',
   # 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/PRESUBMIT.py b/PRESUBMIT.py
index da46670..861990e 100644
--- a/PRESUBMIT.py
+++ b/PRESUBMIT.py
@@ -1925,7 +1925,7 @@
   return []
 
 
-def _CheckNoDeprecatedCompiledResourcesGYP(input_api, output_api):
+def _CheckNoDeprecatedCompiledResourcesGyp(input_api, output_api):
   """Checks for old style compiled_resources.gyp files."""
   is_compiled_resource = lambda fp: fp.endswith('compiled_resources.gyp')
 
@@ -1969,7 +1969,7 @@
   ( "-webkit-repeating-radial-gradient", "repeating-radial-gradient" ),
 ]
 
-def _CheckNoDeprecatedCSS(input_api, output_api):
+def _CheckNoDeprecatedCss(input_api, output_api):
   """ Make sure that we don't use deprecated CSS
       properties, functions or values. Our external
       documentation and iOS CSS for dom distiller
@@ -2004,7 +2004,7 @@
   ( "__defineSetter__", "Object.defineProperty" ),
 ]
 
-def _CheckNoDeprecatedJS(input_api, output_api):
+def _CheckNoDeprecatedJs(input_api, output_api):
   """Make sure that we don't use deprecated JS in Chrome code."""
   results = []
   file_inclusion_pattern = (r".+\.js$",)  # TODO(dbeam): .html?
@@ -2022,6 +2022,27 @@
   return results
 
 
+def _CheckForRiskyJsFeatures(input_api, output_api):
+  maybe_ios_js = (r"^(ios|components|ui\/webui\/resources)\/.+\.js$", )
+  file_filter = lambda f: input_api.FilterSourceFile(f, white_list=maybe_ios_js)
+
+  arrow_lines = []
+  for f in input_api.AffectedFiles(file_filter=file_filter):
+    for lnum, line in f.ChangedContents():
+      if ' => ' in line:
+        arrow_lines.append((f.LocalPath(), lnum))
+
+  if not arrow_lines:
+    return []
+
+  return [output_api.PresubmitPromptWarning("""
+Use of => operator detected in:
+%s
+Please ensure your code does not run on iOS9 (=> (arrow) does not work there).
+https://chromium.googlesource.com/chromium/src/+/master/docs/es6_chromium.md#Arrow-Functions
+""" % "\n".join("  %s:%d\n" % line for line in arrow_lines))]
+
+
 def _AndroidSpecificOnUploadChecks(input_api, output_api):
   """Groups checks that target android code."""
   results = []
@@ -2070,18 +2091,19 @@
   results.extend(_CheckForAnonymousVariables(input_api, output_api))
   results.extend(_CheckCygwinShell(input_api, output_api))
   results.extend(_CheckUserActionUpdate(input_api, output_api))
-  results.extend(_CheckNoDeprecatedCSS(input_api, output_api))
-  results.extend(_CheckNoDeprecatedJS(input_api, output_api))
+  results.extend(_CheckNoDeprecatedCss(input_api, output_api))
+  results.extend(_CheckNoDeprecatedJs(input_api, output_api))
   results.extend(_CheckParseErrors(input_api, output_api))
   results.extend(_CheckForIPCRules(input_api, output_api))
   results.extend(_CheckForWindowsLineEndings(input_api, output_api))
   results.extend(_CheckSingletonInHeaders(input_api, output_api))
-  results.extend(_CheckNoDeprecatedCompiledResourcesGYP(input_api, output_api))
+  results.extend(_CheckNoDeprecatedCompiledResourcesGyp(input_api, output_api))
   results.extend(_CheckPydepsNeedsUpdating(input_api, output_api))
   results.extend(_CheckJavaStyle(input_api, output_api))
   results.extend(_CheckIpcOwners(input_api, output_api))
   results.extend(_CheckMojoUsesNewWrapperTypes(input_api, output_api))
   results.extend(_CheckUselessForwardDeclarations(input_api, output_api))
+  results.extend(_CheckForRiskyJsFeatures(input_api, output_api))
 
   if any('PRESUBMIT.py' == f.LocalPath() for f in input_api.AffectedFiles()):
     results.extend(input_api.canned_checks.RunUnitTestsInDirectory(
diff --git a/PRESUBMIT_test.py b/PRESUBMIT_test.py
index e5f6215..34da837 100755
--- a/PRESUBMIT_test.py
+++ b/PRESUBMIT_test.py
@@ -412,16 +412,16 @@
     self.assertEqual(0, len(warnings))
 
 
-class CheckNoDeprecatedCompiledResourcesGYPTest(unittest.TestCase):
-  def testNoDeprecatedCompiledResourcsGYP(self):
+class CheckNoDeprecatedCompiledResourcesGypTest(unittest.TestCase):
+  def testNoDeprecatedCompiledResourcsGyp(self):
     mock_input_api = MockInputApi()
     mock_input_api.files = [MockFile('some/js/compiled_resources.gyp', [])]
-    errors = PRESUBMIT._CheckNoDeprecatedCompiledResourcesGYP(mock_input_api,
+    errors = PRESUBMIT._CheckNoDeprecatedCompiledResourcesGyp(mock_input_api,
                                                               MockOutputApi())
     self.assertEquals(1, len(errors))
 
     mock_input_api.files = [MockFile('some/js/compiled_resources2.gyp', [])]
-    errors = PRESUBMIT._CheckNoDeprecatedCompiledResourcesGYP(mock_input_api,
+    errors = PRESUBMIT._CheckNoDeprecatedCompiledResourcesGyp(mock_input_api,
                                                               MockOutputApi())
     self.assertEquals(0, len(errors))
 
@@ -1188,5 +1188,41 @@
     self.assertEqual(4, len(warnings))
 
 
+class RiskyJsTest(unittest.TestCase):
+  def testArrowWarnInIos9Code(self):
+    mock_input_api = MockInputApi()
+    mock_output_api = MockOutputApi()
+
+    mock_input_api.files = [
+      MockAffectedFile('components/blah.js', ["shouldn't use => here"]),
+    ]
+    warnings = PRESUBMIT._CheckForRiskyJsFeatures(
+        mock_input_api, mock_output_api)
+    self.assertEqual(1, len(warnings))
+
+    mock_input_api.files = [
+      MockAffectedFile('ios/blee.js', ['might => break folks']),
+    ]
+    warnings = PRESUBMIT._CheckForRiskyJsFeatures(
+        mock_input_api, mock_output_api)
+    self.assertEqual(1, len(warnings))
+
+    mock_input_api.files = [
+      MockAffectedFile('ui/webui/resources/blarg.js', ['on => iOS9']),
+    ]
+    warnings = PRESUBMIT._CheckForRiskyJsFeatures(
+        mock_input_api, mock_output_api)
+    self.assertEqual(1, len(warnings))
+
+  def testArrowsAllowedInChromeCode(self):
+    mock_input_api = MockInputApi()
+    mock_input_api.files = [
+      MockAffectedFile('chrome/browser/resources/blah.js', 'arrow => OK here'),
+    ]
+    warnings = PRESUBMIT._CheckForRiskyJsFeatures(
+        mock_input_api, MockOutputApi())
+    self.assertEqual(0, len(warnings))
+
+
 if __name__ == '__main__':
   unittest.main()
diff --git a/android_webview/browser/DEPS b/android_webview/browser/DEPS
index d601fd2a..7ab5e1d 100644
--- a/android_webview/browser/DEPS
+++ b/android_webview/browser/DEPS
@@ -44,6 +44,8 @@
 
   "+services/service_manager/public/cpp",
 
+  "+storage/browser/quota",
+
   "+ui/gfx",
   "+ui/gl",
 
diff --git a/android_webview/browser/aw_content_browser_client.cc b/android_webview/browser/aw_content_browser_client.cc
index a6bb565..5f90c45 100644
--- a/android_webview/browser/aw_content_browser_client.cc
+++ b/android_webview/browser/aw_content_browser_client.cc
@@ -47,6 +47,7 @@
 #include "content/public/browser/render_frame_host.h"
 #include "content/public/browser/render_process_host.h"
 #include "content/public/browser/render_view_host.h"
+#include "content/public/browser/storage_partition.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/common/content_switches.h"
 #include "content/public/common/service_names.mojom.h"
@@ -58,6 +59,7 @@
 #include "net/ssl/ssl_cert_request_info.h"
 #include "net/ssl/ssl_info.h"
 #include "services/service_manager/public/cpp/interface_registry.h"
+#include "storage/browser/quota/quota_settings.h"
 #include "ui/base/resource/resource_bundle.h"
 #include "ui/base/resource/resource_bundle_android.h"
 #include "ui/resources/grit/ui_resources.h"
@@ -353,6 +355,17 @@
   return new AwQuotaPermissionContext;
 }
 
+void AwContentBrowserClient::GetQuotaSettings(
+    content::BrowserContext* context,
+    content::StoragePartition* partition,
+    const storage::OptionalQuotaSettingsCallback& callback) {
+  content::BrowserThread::PostTaskAndReplyWithResult(
+      content::BrowserThread::FILE, FROM_HERE,
+      base::Bind(&storage::CalculateNominalDynamicSettings,
+                 partition->GetPath(), context->IsOffTheRecord()),
+      callback);
+}
+
 void AwContentBrowserClient::AllowCertificateError(
     content::WebContents* web_contents,
     int cert_error,
diff --git a/android_webview/browser/aw_content_browser_client.h b/android_webview/browser/aw_content_browser_client.h
index a3472c5..6b8c6638 100644
--- a/android_webview/browser/aw_content_browser_client.h
+++ b/android_webview/browser/aw_content_browser_client.h
@@ -74,6 +74,10 @@
       content::ResourceContext* context,
       const std::vector<std::pair<int, int>>& render_frames) override;
   content::QuotaPermissionContext* CreateQuotaPermissionContext() override;
+  void GetQuotaSettings(
+      content::BrowserContext* context,
+      content::StoragePartition* partition,
+      const storage::OptionalQuotaSettingsCallback& callback) override;
   void AllowCertificateError(
       content::WebContents* web_contents,
       int cert_error,
diff --git a/ash/common/system/tray/fixed_sized_scroll_view.cc b/ash/common/system/tray/fixed_sized_scroll_view.cc
index 33fcef69..d3c4a35 100644
--- a/ash/common/system/tray/fixed_sized_scroll_view.cc
+++ b/ash/common/system/tray/fixed_sized_scroll_view.cc
@@ -5,7 +5,6 @@
 #include "ash/common/system/tray/fixed_sized_scroll_view.h"
 
 #include "ash/common/material_design/material_design_controller.h"
-#include "ui/views/controls/scrollbar/overlay_scroll_bar.h"
 
 namespace ash {
 
@@ -19,10 +18,6 @@
 
 FixedSizedScrollView::FixedSizedScrollView() {
   set_notify_enter_exit_on_child(true);
-  if (UseMd()) {
-    SetVerticalScrollBar(new views::OverlayScrollBar(false));
-    SetHorizontalScrollBar(new views::OverlayScrollBar(true));
-  }
 }
 
 FixedSizedScrollView::~FixedSizedScrollView() {}
diff --git a/ash/shell/content/client/DEPS b/ash/shell/content/client/DEPS
index f71262e8..08c4461 100644
--- a/ash/shell/content/client/DEPS
+++ b/ash/shell/content/client/DEPS
@@ -1,4 +1,5 @@
 include_rules = [
   "+content/public",
   "+content/shell",
+  "+storage/browser/quota",
 ]
diff --git a/ash/shell/content/client/shell_content_browser_client.cc b/ash/shell/content/client/shell_content_browser_client.cc
index 60da706..7cb284d 100644
--- a/ash/shell/content/client/shell_content_browser_client.cc
+++ b/ash/shell/content/client/shell_content_browser_client.cc
@@ -8,6 +8,10 @@
 
 #include "ash/shell/content/client/shell_browser_main_parts.h"
 #include "base/command_line.h"
+#include "content/public/browser/browser_context.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/storage_partition.h"
+#include "storage/browser/quota/quota_settings.h"
 #include "third_party/skia/include/core/SkBitmap.h"
 
 namespace ash {
@@ -24,5 +28,16 @@
   return shell_browser_main_parts_;
 }
 
+void ShellContentBrowserClient::GetQuotaSettings(
+    content::BrowserContext* context,
+    content::StoragePartition* partition,
+    const storage::OptionalQuotaSettingsCallback& callback) {
+  content::BrowserThread::PostTaskAndReplyWithResult(
+      content::BrowserThread::FILE, FROM_HERE,
+      base::Bind(&storage::CalculateNominalDynamicSettings,
+                 partition->GetPath(), context->IsOffTheRecord()),
+      callback);
+}
+
 }  // namespace examples
 }  // namespace views
diff --git a/ash/shell/content/client/shell_content_browser_client.h b/ash/shell/content/client/shell_content_browser_client.h
index 29752753..c91183e3 100644
--- a/ash/shell/content/client/shell_content_browser_client.h
+++ b/ash/shell/content/client/shell_content_browser_client.h
@@ -28,6 +28,10 @@
   // Overridden from content::ContentBrowserClient:
   content::BrowserMainParts* CreateBrowserMainParts(
       const content::MainFunctionParams& parameters) override;
+  void GetQuotaSettings(
+      content::BrowserContext* context,
+      content::StoragePartition* partition,
+      const storage::OptionalQuotaSettingsCallback& callback) override;
 
  private:
   ShellBrowserMainParts* shell_browser_main_parts_;
diff --git a/base/time/time.cc b/base/time/time.cc
index df3942c..9fb9cdfa 100644
--- a/base/time/time.cc
+++ b/base/time/time.cc
@@ -21,11 +21,6 @@
 
 // TimeDelta ------------------------------------------------------------------
 
-// static
-TimeDelta TimeDelta::Max() {
-  return TimeDelta(std::numeric_limits<int64_t>::max());
-}
-
 int TimeDelta::InDays() const {
   if (is_max()) {
     // Preserve max to prevent overflow.
diff --git a/base/time/time.h b/base/time/time.h
index ece4fe8..74aff23 100644
--- a/base/time/time.h
+++ b/base/time/time.h
@@ -130,7 +130,7 @@
   // Returns the maximum time delta, which should be greater than any reasonable
   // time delta we might compare it to. Adding or subtracting the maximum time
   // delta to a time or another time delta has an undefined result.
-  static TimeDelta Max();
+  static constexpr TimeDelta Max();
 
   // Returns the internal numeric value of the TimeDelta object. Please don't
   // use this and do arithmetic on it, as it is more error prone than using the
@@ -671,6 +671,11 @@
 }
 
 // static
+constexpr TimeDelta TimeDelta::Max() {
+  return TimeDelta(std::numeric_limits<int64_t>::max());
+}
+
+// static
 constexpr TimeDelta TimeDelta::FromDouble(double value) {
   // TODO(crbug.com/612601): Use saturated_cast<int64_t>(value) once we sort out
   // the Min() behavior.
diff --git a/blimp/engine/DEPS b/blimp/engine/DEPS
index 7e4c0fe..50d5e50bd 100644
--- a/blimp/engine/DEPS
+++ b/blimp/engine/DEPS
@@ -14,6 +14,7 @@
   "+mojo/public",
   "+net",
   "+services/service_manager/public/cpp",
+  "+storage/browser/quota",
   "+third_party/blimp_fonts",
   "+third_party/khronos/GLES2/gl2.h",
   "+third_party/WebKit/public/platform/WebGestureEvent.h",
diff --git a/blimp/engine/app/blimp_content_browser_client.cc b/blimp/engine/app/blimp_content_browser_client.cc
index 5e8c42b3..0a94405 100644
--- a/blimp/engine/app/blimp_content_browser_client.cc
+++ b/blimp/engine/app/blimp_content_browser_client.cc
@@ -8,9 +8,12 @@
 #include "blimp/engine/app/settings_manager.h"
 #include "blimp/engine/grit/blimp_browser_resources.h"
 #include "blimp/engine/mojo/blob_channel_service.h"
+#include "content/public/browser/browser_context.h"
 #include "content/public/browser/browser_thread.h"
+#include "content/public/browser/storage_partition.h"
 #include "content/public/common/service_names.mojom.h"
 #include "services/service_manager/public/cpp/interface_registry.h"
+#include "storage/browser/quota/quota_settings.h"
 #include "ui/base/resource/resource_bundle.h"
 
 namespace blimp {
@@ -69,5 +72,16 @@
   return base::JSONReader::Read(manifest_contents);
 }
 
+void BlimpContentBrowserClient::GetQuotaSettings(
+    content::BrowserContext* context,
+    content::StoragePartition* partition,
+    const storage::OptionalQuotaSettingsCallback& callback) {
+  content::BrowserThread::PostTaskAndReplyWithResult(
+      content::BrowserThread::FILE, FROM_HERE,
+      base::Bind(&storage::CalculateNominalDynamicSettings,
+                 partition->GetPath(), context->IsOffTheRecord()),
+      callback);
+}
+
 }  // namespace engine
 }  // namespace blimp
diff --git a/blimp/engine/app/blimp_content_browser_client.h b/blimp/engine/app/blimp_content_browser_client.h
index d843da0..0aa2582 100644
--- a/blimp/engine/app/blimp_content_browser_client.h
+++ b/blimp/engine/app/blimp_content_browser_client.h
@@ -33,6 +33,10 @@
       content::RenderProcessHost* render_process_host) override;
   std::unique_ptr<base::Value> GetServiceManifestOverlay(
       const std::string& name) override;
+  void GetQuotaSettings(
+      content::BrowserContext* context,
+      content::StoragePartition* partition,
+      const storage::OptionalQuotaSettingsCallback& callback) override;
 
   BlimpBrowserContext* GetBrowserContext();
 
diff --git a/build/sanitizers/tsan_suppressions.cc b/build/sanitizers/tsan_suppressions.cc
index dbccde90..e5eefafe 100644
--- a/build/sanitizers/tsan_suppressions.cc
+++ b/build/sanitizers/tsan_suppressions.cc
@@ -270,6 +270,9 @@
 // http://crbug.com/638583
 "race:webrtc/modules/audio_processing/aec/aec_rdft.cc\n"
 
+// http://crbug.com/587199
+"race:base::TimerTest_OneShotTimer_CustomTaskRunner_Test::TestBody\n"
+
 // End of suppressions.
 ;  // Please keep this semicolon.
 
diff --git a/chrome/VERSION b/chrome/VERSION
index 653ceaf..bed3715 100644
--- a/chrome/VERSION
+++ b/chrome/VERSION
@@ -1,4 +1,4 @@
 MAJOR=57
 MINOR=0
-BUILD=2952
+BUILD=2953
 PATCH=0
diff --git a/chrome/app/theme/default_100_percent/common/update_menu_severity_high.png b/chrome/app/theme/default_100_percent/common/update_menu_severity_high.png
deleted file mode 100644
index e6a259f..0000000
--- a/chrome/app/theme/default_100_percent/common/update_menu_severity_high.png
+++ /dev/null
Binary files differ
diff --git a/chrome/app/theme/default_100_percent/common/update_menu_severity_low.png b/chrome/app/theme/default_100_percent/common/update_menu_severity_low.png
deleted file mode 100644
index 9c6c8a5d..0000000
--- a/chrome/app/theme/default_100_percent/common/update_menu_severity_low.png
+++ /dev/null
Binary files differ
diff --git a/chrome/app/theme/default_100_percent/common/update_menu_severity_medium.png b/chrome/app/theme/default_100_percent/common/update_menu_severity_medium.png
deleted file mode 100644
index 29e42af..0000000
--- a/chrome/app/theme/default_100_percent/common/update_menu_severity_medium.png
+++ /dev/null
Binary files differ
diff --git a/chrome/app/theme/default_200_percent/common/update_menu_severity_high.png b/chrome/app/theme/default_200_percent/common/update_menu_severity_high.png
deleted file mode 100644
index 1c00329c..0000000
--- a/chrome/app/theme/default_200_percent/common/update_menu_severity_high.png
+++ /dev/null
Binary files differ
diff --git a/chrome/app/theme/default_200_percent/common/update_menu_severity_low.png b/chrome/app/theme/default_200_percent/common/update_menu_severity_low.png
deleted file mode 100644
index 2374b69a7..0000000
--- a/chrome/app/theme/default_200_percent/common/update_menu_severity_low.png
+++ /dev/null
Binary files differ
diff --git a/chrome/app/theme/default_200_percent/common/update_menu_severity_medium.png b/chrome/app/theme/default_200_percent/common/update_menu_severity_medium.png
deleted file mode 100644
index fbbcefd4..0000000
--- a/chrome/app/theme/default_200_percent/common/update_menu_severity_medium.png
+++ /dev/null
Binary files differ
diff --git a/chrome/app/theme/theme_resources.grd b/chrome/app/theme/theme_resources.grd
index 7ad26d30..724e8ab 100644
--- a/chrome/app/theme/theme_resources.grd
+++ b/chrome/app/theme/theme_resources.grd
@@ -627,9 +627,6 @@
         <structure type="chrome_scaled_image" name="IDR_TRANSLATE" file="legacy/translate.png" />
         <structure type="chrome_scaled_image" name="IDR_TRANSLATE_ACTIVE" file="legacy/translate_active.png" />
       </if>
-      <structure type="chrome_scaled_image" name="IDR_UPDATE_MENU_SEVERITY_LOW" file="common/update_menu_severity_low.png" />
-      <structure type="chrome_scaled_image" name="IDR_UPDATE_MENU_SEVERITY_MEDIUM" file="common/update_menu_severity_medium.png" />
-      <structure type="chrome_scaled_image" name="IDR_UPDATE_MENU_SEVERITY_HIGH" file="common/update_menu_severity_high.png" />
       <structure type="chrome_scaled_image" name="IDR_USB_NOTIFICATION_ICON" file="common/notification_usb_icon.png" />
       <if expr="chromeos">
         <structure type="chrome_scaled_image" name="IDR_USER_IMAGE_CAPTURE" file="cros/snapshot_wide.png" />
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index 828c551..0acff67 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -354,8 +354,6 @@
     "download/save_package_file_picker.h",
     "engagement/important_sites_util.cc",
     "engagement/important_sites_util.h",
-    "engagement/site_engagement_eviction_policy.cc",
-    "engagement/site_engagement_eviction_policy.h",
     "engagement/site_engagement_helper.cc",
     "engagement/site_engagement_helper.h",
     "engagement/site_engagement_metrics.cc",
@@ -1298,6 +1296,8 @@
     "usb/web_usb_permission_provider.h",
     "web_data_service_factory.cc",
     "web_data_service_factory.h",
+    "webshare/share_service_impl.cc",
+    "webshare/share_service_impl.h",
     "win/app_icon.cc",
     "win/app_icon.h",
     "win/browser_util.cc",
diff --git a/chrome/browser/browsing_data/browsing_data_quota_helper_unittest.cc b/chrome/browser/browsing_data/browsing_data_quota_helper_unittest.cc
index e8a97eb3..2c5781d 100644
--- a/chrome/browser/browsing_data/browsing_data_quota_helper_unittest.cc
+++ b/chrome/browser/browsing_data/browsing_data_quota_helper_unittest.cc
@@ -37,8 +37,8 @@
     quota_manager_ = new storage::QuotaManager(
         false, dir_.GetPath(),
         BrowserThread::GetTaskRunnerForThread(BrowserThread::IO).get(),
-        BrowserThread::GetTaskRunnerForThread(BrowserThread::DB).get(),
-        nullptr);
+        BrowserThread::GetTaskRunnerForThread(BrowserThread::DB).get(), nullptr,
+        storage::GetQuotaSettingsFunc());
     helper_ = new BrowsingDataQuotaHelperImpl(quota_manager_.get());
   }
 
diff --git a/chrome/browser/chrome_content_browser_client.cc b/chrome/browser/chrome_content_browser_client.cc
index 2d7a02d6..66f1290 100644
--- a/chrome/browser/chrome_content_browser_client.cc
+++ b/chrome/browser/chrome_content_browser_client.cc
@@ -48,7 +48,6 @@
 #include "chrome/browser/content_settings/tab_specific_content_settings.h"
 #include "chrome/browser/defaults.h"
 #include "chrome/browser/download/download_prefs.h"
-#include "chrome/browser/engagement/site_engagement_eviction_policy.h"
 #include "chrome/browser/field_trial_recorder.h"
 #include "chrome/browser/font_family_cache.h"
 #include "chrome/browser/media/webrtc/media_capture_devices_dispatcher.h"
@@ -98,6 +97,7 @@
 #include "chrome/browser/ui/webui/chrome_web_ui_controller_factory.h"
 #include "chrome/browser/ui/webui/log_web_ui_url.h"
 #include "chrome/browser/usb/usb_tab_helper.h"
+#include "chrome/browser/webshare/share_service_impl.h"
 #include "chrome/common/channel_info.h"
 #include "chrome/common/chrome_constants.h"
 #include "chrome/common/chrome_paths.h"
@@ -405,6 +405,8 @@
 // thread.
 base::LazyInstance<std::string> g_io_thread_application_locale;
 
+const storage::QuotaSettings* g_default_quota_settings;
+
 #if BUILDFLAG(ENABLE_PLUGINS)
 // TODO(teravest): Add renderer-side API-specific checking for these APIs so
 // that blanket permission isn't granted to all dev channel APIs for these.
@@ -2164,12 +2166,20 @@
   return new ChromeQuotaPermissionContext();
 }
 
-std::unique_ptr<storage::QuotaEvictionPolicy>
-ChromeContentBrowserClient::GetTemporaryStorageEvictionPolicy(
-    content::BrowserContext* context) {
-  return SiteEngagementEvictionPolicy::IsEnabled()
-             ? base::MakeUnique<SiteEngagementEvictionPolicy>(context)
-             : nullptr;
+void ChromeContentBrowserClient::GetQuotaSettings(
+    content::BrowserContext* context,
+    content::StoragePartition* partition,
+    const storage::OptionalQuotaSettingsCallback& callback) {
+  if (g_default_quota_settings) {
+    // For debugging tests harness can inject settings.
+    callback.Run(*g_default_quota_settings);
+    return;
+  }
+  content::BrowserThread::PostTaskAndReplyWithResult(
+      content::BrowserThread::FILE, FROM_HERE,
+      base::Bind(&storage::CalculateNominalDynamicSettings,
+                 partition->GetPath(), context->IsOffTheRecord()),
+      callback);
 }
 
 void ChromeContentBrowserClient::AllowCertificateError(
@@ -3057,6 +3067,11 @@
   }
 #endif
 
+#if defined(OS_LINUX) || defined(OS_WIN)
+  if (!ChromeOriginTrialPolicy().IsFeatureDisabled("WebShare")) {
+    registry->AddInterface(base::Bind(&ShareServiceImpl::Create));
+  }
+#endif
 }
 
 void ChromeContentBrowserClient::ExposeInterfacesToGpuProcess(
@@ -3356,3 +3371,9 @@
   task_scheduler_util::variations::
       MaybePerformBrowserTaskSchedulerRedirection();
 }
+
+//static
+void ChromeContentBrowserClient::SetDefaultQuotaSettingsForTesting(
+    const storage::QuotaSettings* settings) {
+  g_default_quota_settings = settings;
+}
diff --git a/chrome/browser/chrome_content_browser_client.h b/chrome/browser/chrome_content_browser_client.h
index be14cef..352f156 100644
--- a/chrome/browser/chrome_content_browser_client.h
+++ b/chrome/browser/chrome_content_browser_client.h
@@ -171,8 +171,11 @@
       const GURL& url,
       content::ResourceContext* context) override;
   content::QuotaPermissionContext* CreateQuotaPermissionContext() override;
-  std::unique_ptr<storage::QuotaEvictionPolicy>
-  GetTemporaryStorageEvictionPolicy(content::BrowserContext* context) override;
+  void GetQuotaSettings(
+      content::BrowserContext* context,
+      content::StoragePartition* partition,
+      const storage::OptionalQuotaSettingsCallback& callback) override;
+
   void AllowCertificateError(
       content::WebContents* web_contents,
       int cert_error,
@@ -326,6 +329,7 @@
 
  private:
   friend class DisableWebRtcEncryptionFlagTest;
+  friend class InProcessBrowserTest;
 
 #if BUILDFLAG(ENABLE_WEBRTC)
   // Copies disable WebRTC encryption switch depending on the channel.
@@ -356,6 +360,11 @@
       const base::Callback<void(bool)>& callback);
 #endif
 
+  // The value pointed to by |settings| should remain valid until the
+  // the function is called again with a new value or a nullptr.
+  static void SetDefaultQuotaSettingsForTesting(
+      const storage::QuotaSettings *settings);
+
 #if BUILDFLAG(ENABLE_PLUGINS)
   // Set of origins that can use TCP/UDP private APIs from NaCl.
   std::set<std::string> allowed_socket_origins_;
diff --git a/chrome/browser/chromeos/policy/device_status_collector.cc b/chrome/browser/chromeos/policy/device_status_collector.cc
index eee5156..8bfcfec 100644
--- a/chrome/browser/chromeos/policy/device_status_collector.cc
+++ b/chrome/browser/chromeos/policy/device_status_collector.cc
@@ -109,11 +109,17 @@
   std::vector<em::VolumeInfo> result;
   for (const std::string& mount_point : mount_points) {
     base::FilePath mount_path(mount_point);
+
+    // Non-native file systems do not have a mount point in the local file
+    // system. However, it's worth checking here, as it's easier than checking
+    // earlier which mount point is local, and which one is not.
+    if (mount_point.empty() || !base::PathExists(mount_path))
+      continue;
+
     int64_t free_size = base::SysInfo::AmountOfFreeDiskSpace(mount_path);
     int64_t total_size = base::SysInfo::AmountOfTotalDiskSpace(mount_path);
     if (free_size < 0 || total_size < 0) {
-      LOG_IF(ERROR, !mount_point.empty()) << "Unable to get volume status for "
-                                          << mount_point;
+      LOG(ERROR) << "Unable to get volume status for " << mount_point;
       continue;
     }
     em::VolumeInfo info;
diff --git a/chrome/browser/engagement/site_engagement_eviction_policy.cc b/chrome/browser/engagement/site_engagement_eviction_policy.cc
deleted file mode 100644
index 7d52f56..0000000
--- a/chrome/browser/engagement/site_engagement_eviction_policy.cc
+++ /dev/null
@@ -1,144 +0,0 @@
-// Copyright 2015 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/engagement/site_engagement_eviction_policy.h"
-
-#include "base/command_line.h"
-#include "base/metrics/field_trial.h"
-#include "base/strings/string_util.h"
-#include "chrome/browser/browser_process.h"
-#include "chrome/browser/engagement/site_engagement_service.h"
-#include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/profiles/profile_manager.h"
-#include "chrome/common/chrome_switches.h"
-#include "content/public/browser/browser_thread.h"
-
-namespace {
-
-const int kExpectedEngagementSites = 200;
-
-// Gets the quota that an origin deserves based on its site engagement.
-int64_t GetSoftQuotaForOrigin(const GURL& origin,
-                              int score,
-                              int total_engagement_points,
-                              int64_t global_quota) {
-  double quota_per_point =
-      global_quota /
-      std::max(kExpectedEngagementSites * SiteEngagementService::GetMaxPoints(),
-               static_cast<double>(total_engagement_points));
-
-  return score * quota_per_point;
-}
-
-GURL DoCalculateEvictionOrigin(
-    const scoped_refptr<storage::SpecialStoragePolicy>& special_storage_policy,
-    SiteEngagementScoreProvider* score_provider,
-    const std::set<GURL>& exceptions,
-    const std::map<GURL, int64_t>& usage_map,
-    int64_t global_quota) {
-  // TODO(calamity): Integrate storage access frequency as an input to this
-  // heuristic.
-
-  // This heuristic is intended to optimize for two criteria:
-  // - evict the site that the user cares about least
-  // - evict the least number of sites to get under the quota limit
-  //
-  // The heuristic for deciding the next eviction origin calculates a soft
-  // quota for each origin which is the amount the origin should be allowed to
-  // use based on its engagement and the global quota. The origin that most
-  // exceeds its soft quota is chosen.
-  GURL origin_to_evict;
-  int64_t max_overuse = std::numeric_limits<int64_t>::min();
-  int total_engagement_points = score_provider->GetTotalEngagementPoints();
-
-  for (const auto& usage : usage_map) {
-    GURL origin = usage.first;
-    if (special_storage_policy &&
-        (special_storage_policy->IsStorageUnlimited(origin) ||
-         special_storage_policy->IsStorageDurable(origin))) {
-      continue;
-    }
-
-    // |overuse| can be negative if the soft quota exceeds the usage.
-    int64_t overuse =
-        usage.second -
-        GetSoftQuotaForOrigin(origin, score_provider->GetScore(origin),
-                              total_engagement_points, global_quota);
-    if (overuse > max_overuse && !base::ContainsKey(exceptions, origin)) {
-      max_overuse = overuse;
-      origin_to_evict = origin;
-    }
-  }
-
-  return origin_to_evict;
-}
-
-GURL GetSiteEngagementEvictionOriginOnUIThread(
-    const scoped_refptr<storage::SpecialStoragePolicy>& special_storage_policy,
-    content::BrowserContext* browser_context,
-    const std::set<GURL>& exceptions,
-    const std::map<GURL, int64_t>& usage_map,
-    int64_t global_quota) {
-  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
-
-  Profile* profile = Profile::FromBrowserContext(browser_context);
-  SiteEngagementScoreProvider* score_provider =
-      g_browser_process->profile_manager()->IsValidProfile(profile)
-          ? SiteEngagementService::Get(profile)
-          : nullptr;
-
-  if (!score_provider)
-    return GURL();
-
-  return DoCalculateEvictionOrigin(special_storage_policy, score_provider,
-                                   exceptions, usage_map, global_quota);
-}
-
-}  // namespace
-
-// static
-bool SiteEngagementEvictionPolicy::IsEnabled() {
-  if (base::CommandLine::ForCurrentProcess()->HasSwitch(
-      switches::kEnableSiteEngagementEvictionPolicy)) {
-    return true;
-  }
-
-  const std::string group_name = base::FieldTrialList::FindFullName(
-      SiteEngagementService::kEngagementParams);
-  return base::StartsWith(group_name, "StorageEvictionEnabled",
-                          base::CompareCase::SENSITIVE);
-}
-
-SiteEngagementEvictionPolicy::SiteEngagementEvictionPolicy(
-    content::BrowserContext* browser_context)
-    : browser_context_(browser_context) {}
-
-SiteEngagementEvictionPolicy::~SiteEngagementEvictionPolicy() {}
-
-void SiteEngagementEvictionPolicy::GetEvictionOrigin(
-    const scoped_refptr<storage::SpecialStoragePolicy>& special_storage_policy,
-    const std::set<GURL>& exceptions,
-    const std::map<GURL, int64_t>& usage_map,
-    int64_t global_quota,
-    const storage::GetOriginCallback& callback) {
-  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
-
-  content::BrowserThread::PostTaskAndReplyWithResult(
-      content::BrowserThread::UI, FROM_HERE,
-      base::Bind(&GetSiteEngagementEvictionOriginOnUIThread,
-                 special_storage_policy, browser_context_, exceptions,
-                 usage_map, global_quota),
-      callback);
-}
-
-// static
-GURL SiteEngagementEvictionPolicy::CalculateEvictionOriginForTests(
-    const scoped_refptr<storage::SpecialStoragePolicy>& special_storage_policy,
-    SiteEngagementScoreProvider* score_provider,
-    const std::set<GURL>& exceptions,
-    const std::map<GURL, int64_t>& usage_map,
-    int64_t global_quota) {
-  return DoCalculateEvictionOrigin(special_storage_policy, score_provider,
-                                   exceptions, usage_map, global_quota);
-}
diff --git a/chrome/browser/engagement/site_engagement_eviction_policy.h b/chrome/browser/engagement/site_engagement_eviction_policy.h
deleted file mode 100644
index 4266449..0000000
--- a/chrome/browser/engagement/site_engagement_eviction_policy.h
+++ /dev/null
@@ -1,56 +0,0 @@
-// Copyright 2015 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_BROWSER_ENGAGEMENT_SITE_ENGAGEMENT_EVICTION_POLICY_H_
-#define CHROME_BROWSER_ENGAGEMENT_SITE_ENGAGEMENT_EVICTION_POLICY_H_
-
-#include <stdint.h>
-
-#include <map>
-#include <memory>
-
-#include "base/macros.h"
-#include "base/memory/ref_counted.h"
-#include "storage/browser/quota/quota_manager.h"
-#include "url/gurl.h"
-
-namespace content {
-class BrowserContext;
-}
-
-class SiteEngagementScoreProvider;
-
-class SiteEngagementEvictionPolicy : public storage::QuotaEvictionPolicy {
- public:
-  static bool IsEnabled();
-
-  explicit SiteEngagementEvictionPolicy(
-      content::BrowserContext* browser_context);
-  ~SiteEngagementEvictionPolicy() override;
-
-  // Overridden from storage::QuotaEvictionPolicy:
-  void GetEvictionOrigin(const scoped_refptr<storage::SpecialStoragePolicy>&
-                             special_storage_policy,
-                         const std::set<GURL>& exceptions,
-                         const std::map<GURL, int64_t>& usage_map,
-                         int64_t global_quota,
-                         const storage::GetOriginCallback& callback) override;
-
- private:
-  friend class SiteEngagementEvictionPolicyTest;
-
-  static GURL CalculateEvictionOriginForTests(
-      const scoped_refptr<storage::SpecialStoragePolicy>&
-          special_storage_policy,
-      SiteEngagementScoreProvider* score_provider,
-      const std::set<GURL>& exceptions,
-      const std::map<GURL, int64_t>& usage_map,
-      int64_t global_quota);
-
-  content::BrowserContext* const browser_context_;
-
-  DISALLOW_COPY_AND_ASSIGN(SiteEngagementEvictionPolicy);
-};
-
-#endif  // CHROME_BROWSER_ENGAGEMENT_SITE_ENGAGEMENT_EVICTION_POLICY_H_
diff --git a/chrome/browser/engagement/site_engagement_eviction_policy_unittest.cc b/chrome/browser/engagement/site_engagement_eviction_policy_unittest.cc
deleted file mode 100644
index edfb8e1e..0000000
--- a/chrome/browser/engagement/site_engagement_eviction_policy_unittest.cc
+++ /dev/null
@@ -1,173 +0,0 @@
-// Copyright 2015 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/engagement/site_engagement_eviction_policy.h"
-
-#include <stdint.h>
-
-#include <memory>
-
-#include "base/files/scoped_temp_dir.h"
-#include "base/macros.h"
-#include "base/memory/weak_ptr.h"
-#include "base/run_loop.h"
-#include "base/threading/thread_task_runner_handle.h"
-#include "chrome/browser/engagement/site_engagement_service.h"
-#include "content/public/test/mock_special_storage_policy.h"
-#include "content/public/test/mock_storage_client.h"
-#include "content/public/test/test_browser_thread_bundle.h"
-#include "storage/browser/quota/quota_manager.h"
-#include "storage/browser/quota/quota_manager_proxy.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace {
-
-const int64_t kGlobalQuota = 25 * 1024;
-
-}  // namespace
-
-class TestSiteEngagementScoreProvider : public SiteEngagementScoreProvider {
- public:
-  TestSiteEngagementScoreProvider() {}
-
-  virtual ~TestSiteEngagementScoreProvider() {}
-
-  double GetScore(const GURL& url) const override {
-    const auto& it = engagement_score_map_.find(url);
-    if (it != engagement_score_map_.end())
-      return it->second;
-    return 0.0;
-  }
-
-  double GetTotalEngagementPoints() const override {
-    double total = 0;
-    for (const auto& site : engagement_score_map_)
-      total += site.second;
-    return total;
-  }
-
-  void SetScore(const GURL& origin, double score) {
-    engagement_score_map_[origin] = score;
-  }
-
- private:
-  std::map<GURL, double> engagement_score_map_;
-
-  DISALLOW_COPY_AND_ASSIGN(TestSiteEngagementScoreProvider);
-};
-
-class SiteEngagementEvictionPolicyTest : public testing::Test {
- public:
-  SiteEngagementEvictionPolicyTest()
-      : score_provider_(new TestSiteEngagementScoreProvider()),
-        storage_policy_(new content::MockSpecialStoragePolicy()) {}
-
-  ~SiteEngagementEvictionPolicyTest() override {}
-
-  GURL CalculateEvictionOriginWithExceptions(
-      const std::map<GURL, int64_t>& usage,
-      const std::set<GURL>& exceptions) {
-    return SiteEngagementEvictionPolicy::CalculateEvictionOriginForTests(
-        storage_policy_, score_provider_.get(), exceptions, usage,
-        kGlobalQuota);
-  }
-
-  GURL CalculateEvictionOrigin(const std::map<GURL, int64_t>& usage) {
-    return CalculateEvictionOriginWithExceptions(usage, std::set<GURL>());
-  }
-
-  TestSiteEngagementScoreProvider* score_provider() {
-    return score_provider_.get();
-  }
-
-  content::MockSpecialStoragePolicy* storage_policy() {
-    return storage_policy_.get();
-  }
-
- private:
-  std::unique_ptr<TestSiteEngagementScoreProvider> score_provider_;
-  scoped_refptr<content::MockSpecialStoragePolicy> storage_policy_;
-
-  DISALLOW_COPY_AND_ASSIGN(SiteEngagementEvictionPolicyTest);
-};
-
-TEST_F(SiteEngagementEvictionPolicyTest, GetEvictionOrigin) {
-  GURL url1("http://www.google.com");
-  GURL url2("http://www.example.com");
-  GURL url3("http://www.spam.me");
-
-  std::map<GURL, int64_t> usage;
-  usage[url1] = 10 * 1024;
-  usage[url2] = 10 * 1024;
-  usage[url3] = 10 * 1024;
-
-  score_provider()->SetScore(url1, 50);
-  score_provider()->SetScore(url2, 25);
-
-  // When 3 sites have equal usage, evict the site with the least engagement.
-  EXPECT_EQ(url3, CalculateEvictionOrigin(usage));
-
-  usage[url2] = usage[url3] + 10;
-
-  // Now |url2| has the most usage but |url3| has the least engagement score so
-  // one of them should be evicted. In this case the heuristic chooses |url3|.
-  EXPECT_EQ(url3, CalculateEvictionOrigin(usage));
-
-  // But exceeding allocated usage too much will still result in being evicted
-  // even though the engagement with |url2| is higher.
-  usage[url2] = 15 * 1024;
-  EXPECT_EQ(url2, CalculateEvictionOrigin(usage));
-
-  // When all origins have the same engagement, the origin with the highest
-  // usage is evicted.
-  score_provider()->SetScore(url1, 50);
-  score_provider()->SetScore(url2, 50);
-  score_provider()->SetScore(url3, 50);
-
-  usage[url2] = 10 * 1024;
-  usage[url3] = 20 * 1024;
-  EXPECT_EQ(url3, CalculateEvictionOrigin(usage));
-}
-
-// Test that durable and unlimited storage origins are exempt from eviction.
-TEST_F(SiteEngagementEvictionPolicyTest, SpecialStoragePolicy) {
-  GURL url1("http://www.google.com");
-  GURL url2("http://www.example.com");
-
-  std::map<GURL, int64_t> usage;
-  usage[url1] = 10 * 1024;
-  usage[url2] = 10 * 1024;
-
-  score_provider()->SetScore(url1, 50);
-  score_provider()->SetScore(url2, 25);
-
-  EXPECT_EQ(url2, CalculateEvictionOrigin(usage));
-
-  // Durable storage doesn't get evicted.
-  storage_policy()->AddDurable(url2);
-  EXPECT_EQ(url1, CalculateEvictionOrigin(usage));
-
-  // Unlimited storage doesn't get evicted.
-  storage_policy()->AddUnlimited(url1);
-  EXPECT_EQ(GURL(), CalculateEvictionOrigin(usage));
-}
-
-TEST_F(SiteEngagementEvictionPolicyTest, Exceptions) {
-  GURL url1("http://www.google.com");
-  GURL url2("http://www.example.com");
-
-  std::map<GURL, int64_t> usage;
-  usage[url1] = 10 * 1024;
-  usage[url2] = 10 * 1024;
-
-  score_provider()->SetScore(url1, 50);
-  score_provider()->SetScore(url2, 25);
-
-  EXPECT_EQ(url2, CalculateEvictionOrigin(usage));
-
-  // The policy should respect exceptions.
-  std::set<GURL> exceptions;
-  exceptions.insert(url2);
-  EXPECT_EQ(url1, CalculateEvictionOriginWithExceptions(usage, exceptions));
-}
diff --git a/chrome/browser/engagement/site_engagement_service.cc b/chrome/browser/engagement/site_engagement_service.cc
index 3fa29c7..7104d0d 100644
--- a/chrome/browser/engagement/site_engagement_service.cc
+++ b/chrome/browser/engagement/site_engagement_service.cc
@@ -20,7 +20,6 @@
 #include "base/values.h"
 #include "chrome/browser/banners/app_banner_settings_helper.h"
 #include "chrome/browser/content_settings/host_content_settings_map_factory.h"
-#include "chrome/browser/engagement/site_engagement_eviction_policy.h"
 #include "chrome/browser/engagement/site_engagement_metrics.h"
 #include "chrome/browser/engagement/site_engagement_score.h"
 #include "chrome/browser/engagement/site_engagement_service_factory.h"
diff --git a/chrome/browser/extensions/api/downloads/downloads_api_browsertest.cc b/chrome/browser/extensions/api/downloads/downloads_api_browsertest.cc
index a8a9eca7..ab6d4a2a 100644
--- a/chrome/browser/extensions/api/downloads/downloads_api_browsertest.cc
+++ b/chrome/browser/extensions/api/downloads/downloads_api_browsertest.cc
@@ -17,8 +17,8 @@
 #include "base/macros.h"
 #include "base/memory/ptr_util.h"
 #include "base/message_loop/message_loop.h"
+#include "base/run_loop.h"
 #include "base/strings/stringprintf.h"
-#include "base/synchronization/waitable_event.h"
 #include "build/build_config.h"
 #include "chrome/browser/download/download_file_icon_extractor.h"
 #include "chrome/browser/download/download_service.h"
@@ -703,29 +703,25 @@
     }
     // Invoke the fileapi to copy it into the sandboxed filesystem.
     bool result = false;
-    base::WaitableEvent done_event(
-        base::WaitableEvent::ResetPolicy::MANUAL,
-        base::WaitableEvent::InitialState::NOT_SIGNALED);
+    base::RunLoop run_loop;
     BrowserThread::PostTask(
         BrowserThread::IO, FROM_HERE,
-        base::Bind(&CreateFileForTestingOnIOThread,
-                   base::Unretained(context),
-                   path, temp_file,
-                   base::Unretained(&result),
-                   base::Unretained(&done_event)));
+        base::Bind(&CreateFileForTestingOnIOThread, base::Unretained(context),
+                   path, temp_file, base::Unretained(&result),
+                   run_loop.QuitClosure()));
     // Wait for that to finish.
-    done_event.Wait();
+    run_loop.Run();
     base::DeleteFile(temp_file, false);
     return result;
   }
 
  private:
   static void CopyInCompletion(bool* result,
-                               base::WaitableEvent* done_event,
+                               const base::Closure& quit_closure,
                                base::File::Error error) {
     DCHECK_CURRENTLY_ON(BrowserThread::IO);
     *result = error == base::File::FILE_OK;
-    done_event->Signal();
+    BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, quit_closure);
   }
 
   static void CreateFileForTestingOnIOThread(
@@ -733,13 +729,11 @@
       const storage::FileSystemURL& path,
       const base::FilePath& temp_file,
       bool* result,
-      base::WaitableEvent* done_event) {
+      const base::Closure& quit_closure) {
     DCHECK_CURRENTLY_ON(BrowserThread::IO);
     context->operation_runner()->CopyInForeignFile(
         temp_file, path,
-        base::Bind(&CopyInCompletion,
-                   base::Unretained(result),
-                   base::Unretained(done_event)));
+        base::Bind(&CopyInCompletion, base::Unretained(result), quit_closure));
   }
 };
 
diff --git a/chrome/browser/extensions/api/sync_file_system/sync_file_system_apitest.cc b/chrome/browser/extensions/api/sync_file_system/sync_file_system_apitest.cc
index 1ee2199..18b1bf3 100644
--- a/chrome/browser/extensions/api/sync_file_system/sync_file_system_apitest.cc
+++ b/chrome/browser/extensions/api/sync_file_system/sync_file_system_apitest.cc
@@ -18,6 +18,7 @@
 #include "chrome/browser/sync_file_system/sync_status_code.h"
 #include "chrome/browser/sync_file_system/syncable_file_system_util.h"
 #include "chrome/test/base/test_switches.h"
+#include "content/public/browser/storage_partition.h"
 #include "extensions/browser/extension_function.h"
 #include "storage/browser/fileapi/file_system_url.h"
 #include "storage/browser/quota/quota_manager.h"
@@ -40,16 +41,11 @@
  public:
   SyncFileSystemApiTest()
       : mock_remote_service_(NULL),
-        real_minimum_preserved_space_(0),
         real_default_quota_(0) {}
 
   void SetUpInProcessBrowserTestFixture() override {
     ExtensionApiTest::SetUpInProcessBrowserTestFixture();
 
-    real_minimum_preserved_space_ =
-        storage::QuotaManager::kMinimumPreserveForSystem;
-    storage::QuotaManager::kMinimumPreserveForSystem = 0;
-
     // TODO(calvinlo): Update test code after default quota is made const
     // (http://crbug.com/155488).
     real_default_quota_ =
@@ -58,8 +54,6 @@
   }
 
   void TearDownInProcessBrowserTestFixture() override {
-    storage::QuotaManager::kMinimumPreserveForSystem =
-        real_minimum_preserved_space_;
     storage::QuotaManager::kSyncableStorageDefaultHostQuota =
         real_default_quota_;
     ExtensionApiTest::TearDownInProcessBrowserTestFixture();
@@ -81,7 +75,6 @@
 
  private:
   ::testing::NiceMock<MockRemoteFileSyncService>* mock_remote_service_;
-  int64_t real_minimum_preserved_space_;
   int64_t real_default_quota_;
 };
 
diff --git a/chrome/browser/extensions/api/sync_file_system/sync_file_system_browsertest.cc b/chrome/browser/extensions/api/sync_file_system/sync_file_system_browsertest.cc
index 549b4aaa..0e13258 100644
--- a/chrome/browser/extensions/api/sync_file_system/sync_file_system_browsertest.cc
+++ b/chrome/browser/extensions/api/sync_file_system/sync_file_system_browsertest.cc
@@ -18,6 +18,7 @@
 #include "chrome/browser/sync_file_system/sync_file_system_service.h"
 #include "chrome/browser/sync_file_system/sync_file_system_service_factory.h"
 #include "components/drive/service/fake_drive_service.h"
+#include "content/public/browser/storage_partition.h"
 #include "extensions/test/extension_test_message_listener.h"
 #include "extensions/test/result_catcher.h"
 #include "storage/browser/quota/quota_manager.h"
@@ -61,19 +62,6 @@
       : remote_service_(NULL) {
   }
 
-  void SetUpInProcessBrowserTestFixture() override {
-    ExtensionApiTest::SetUpInProcessBrowserTestFixture();
-    real_minimum_preserved_space_ =
-        storage::QuotaManager::kMinimumPreserveForSystem;
-    storage::QuotaManager::kMinimumPreserveForSystem = 0;
-  }
-
-  void TearDownInProcessBrowserTestFixture() override {
-    storage::QuotaManager::kMinimumPreserveForSystem =
-        real_minimum_preserved_space_;
-    ExtensionApiTest::TearDownInProcessBrowserTestFixture();
-  }
-
   scoped_refptr<base::SequencedTaskRunner> MakeSequencedTaskRunner() {
     scoped_refptr<base::SequencedWorkerPool> worker_pool =
         content::BrowserThread::GetBlockingPool();
@@ -157,8 +145,6 @@
 
   drive_backend::SyncEngine* remote_service_;
 
-  int64_t real_minimum_preserved_space_;
-
   DISALLOW_COPY_AND_ASSIGN(SyncFileSystemTest);
 };
 
diff --git a/chrome/browser/extensions/extension_special_storage_policy.cc b/chrome/browser/extensions/extension_special_storage_policy.cc
index 2abb046..331c9a3 100644
--- a/chrome/browser/extensions/extension_special_storage_policy.cc
+++ b/chrome/browser/extensions/extension_special_storage_policy.cc
@@ -114,11 +114,6 @@
   return cookie_settings_->IsCookieSessionOnly(origin);
 }
 
-bool ExtensionSpecialStoragePolicy::CanQueryDiskSize(const GURL& origin) {
-  base::AutoLock locker(lock_);
-  return installed_apps_.Contains(origin);
-}
-
 bool ExtensionSpecialStoragePolicy::HasSessionOnlyOrigins() {
   if (cookie_settings_.get() == NULL)
     return false;
@@ -177,9 +172,6 @@
       extension->is_app()) {
     if (NeedsProtection(extension) && protected_apps_.Add(extension))
       change_flags |= SpecialStoragePolicy::STORAGE_PROTECTED;
-    // FIXME: Does GrantRightsForExtension imply |extension| is installed?
-    if (extension->is_app())
-      installed_apps_.Add(extension);
 
     if (extension->permissions_data()->HasAPIPermission(
             APIPermission::kUnlimitedStorage) &&
@@ -225,9 +217,6 @@
     if (NeedsProtection(extension) && protected_apps_.Remove(extension))
       change_flags |= SpecialStoragePolicy::STORAGE_PROTECTED;
 
-    if (extension->is_app())
-      installed_apps_.Remove(extension);
-
     if (extension->permissions_data()->HasAPIPermission(
             APIPermission::kUnlimitedStorage) &&
         unlimited_extensions_.Remove(extension))
@@ -251,7 +240,6 @@
   {
     base::AutoLock locker(lock_);
     protected_apps_.Clear();
-    installed_apps_.Clear();
     unlimited_extensions_.Clear();
     file_handler_extensions_.Clear();
     isolated_extensions_.Clear();
diff --git a/chrome/browser/extensions/extension_special_storage_policy.h b/chrome/browser/extensions/extension_special_storage_policy.h
index 5f4ffbb..cce5b043 100644
--- a/chrome/browser/extensions/extension_special_storage_policy.h
+++ b/chrome/browser/extensions/extension_special_storage_policy.h
@@ -39,7 +39,6 @@
   bool IsStorageProtected(const GURL& origin) override;
   bool IsStorageUnlimited(const GURL& origin) override;
   bool IsStorageSessionOnly(const GURL& origin) override;
-  bool CanQueryDiskSize(const GURL& origin) override;
   bool HasIsolatedStorage(const GURL& origin) override;
   bool HasSessionOnlyOrigins() override;
   bool IsStorageDurable(const GURL& origin) override;
@@ -88,7 +87,6 @@
 
   base::Lock lock_;  // Synchronize all access to the collections.
   SpecialCollection protected_apps_;
-  SpecialCollection installed_apps_;
   SpecialCollection unlimited_extensions_;
   SpecialCollection file_handler_extensions_;
   SpecialCollection isolated_extensions_;
diff --git a/chrome/browser/extensions/extension_special_storage_policy_unittest.cc b/chrome/browser/extensions/extension_special_storage_policy_unittest.cc
index b25b31f..1916e30 100644
--- a/chrome/browser/extensions/extension_special_storage_policy_unittest.cc
+++ b/chrome/browser/extensions/extension_special_storage_policy_unittest.cc
@@ -251,23 +251,6 @@
   EXPECT_FALSE(policy_->IsStorageUnlimited(GURL("https://bar.wildcards/")));
 }
 
-TEST_F(ExtensionSpecialStoragePolicyTest, CanQueryDiskSize) {
-  const GURL kHttpUrl("http://foo");
-  const GURL kExtensionUrl("chrome-extension://bar");
-  scoped_refptr<Extension> regular_app(CreateRegularApp());
-  scoped_refptr<Extension> protected_app(CreateProtectedApp());
-  scoped_refptr<Extension> unlimited_app(CreateUnlimitedApp());
-  policy_->GrantRightsForExtension(regular_app.get(), NULL);
-  policy_->GrantRightsForExtension(protected_app.get(), NULL);
-  policy_->GrantRightsForExtension(unlimited_app.get(), NULL);
-
-  EXPECT_FALSE(policy_->CanQueryDiskSize(kHttpUrl));
-  EXPECT_FALSE(policy_->CanQueryDiskSize(kExtensionUrl));
-  EXPECT_TRUE(policy_->CanQueryDiskSize(regular_app->url()));
-  EXPECT_TRUE(policy_->CanQueryDiskSize(protected_app->url()));
-  EXPECT_TRUE(policy_->CanQueryDiskSize(unlimited_app->url()));
-}
-
 TEST_F(ExtensionSpecialStoragePolicyTest, HasIsolatedStorage) {
   const GURL kHttpUrl("http://foo");
   const GURL kExtensionUrl("chrome-extension://bar");
diff --git a/chrome/browser/extensions/extension_storage_monitor.cc b/chrome/browser/extensions/extension_storage_monitor.cc
index 7294fbd4..81394f5 100644
--- a/chrome/browser/extensions/extension_storage_monitor.cc
+++ b/chrome/browser/extensions/extension_storage_monitor.cc
@@ -84,17 +84,16 @@
       extension_id, ExtensionRegistry::EVERYTHING);
 }
 
-void LogTemporaryStorageUsage(int64_t usage,
-                              storage::QuotaStatusCode status,
-                              int64_t global_quota) {
-  if (status == storage::kQuotaStatusOk) {
-    int64_t per_app_quota =
-        global_quota / storage::QuotaManager::kPerHostTemporaryPortion;
+void LogTemporaryStorageUsage(
+    scoped_refptr<storage::QuotaManager> quota_manager,
+    int64_t usage) {
+  const storage::QuotaSettings& settings = quota_manager->settings();
+  if (settings.per_host_quota > 0) {
     // Note we use COUNTS_100 (instead of PERCENT) because this can potentially
     // exceed 100%.
     UMA_HISTOGRAM_COUNTS_100(
         "Extensions.HostedAppUnlimitedStorageTemporaryStorageUsage",
-        100.0 * usage / per_app_quota);
+        100.0 * usage / settings.per_host_quota);
   }
 }
 
@@ -235,12 +234,9 @@
       } else {
         // We can't use the quota in the event because it assumes unlimited
         // storage.
-        BrowserThread::PostTask(
-            BrowserThread::IO,
-            FROM_HERE,
-            base::Bind(&storage::QuotaManager::GetTemporaryGlobalQuota,
-                       state.quota_manager,
-                       base::Bind(&LogTemporaryStorageUsage, event.usage)));
+        BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
+                                base::Bind(&LogTemporaryStorageUsage,
+                                           state.quota_manager, event.usage));
       }
     }
 
diff --git a/chrome/browser/extensions/mock_extension_special_storage_policy.cc b/chrome/browser/extensions/mock_extension_special_storage_policy.cc
index d4d609b..ca32e2d 100644
--- a/chrome/browser/extensions/mock_extension_special_storage_policy.cc
+++ b/chrome/browser/extensions/mock_extension_special_storage_policy.cc
@@ -20,10 +20,6 @@
   return false;
 }
 
-bool MockExtensionSpecialStoragePolicy::CanQueryDiskSize(const GURL& origin) {
-  return false;
-}
-
 bool MockExtensionSpecialStoragePolicy::HasSessionOnlyOrigins() {
   return false;
 }
diff --git a/chrome/browser/extensions/mock_extension_special_storage_policy.h b/chrome/browser/extensions/mock_extension_special_storage_policy.h
index edc142e7..f25676de 100644
--- a/chrome/browser/extensions/mock_extension_special_storage_policy.h
+++ b/chrome/browser/extensions/mock_extension_special_storage_policy.h
@@ -23,7 +23,6 @@
   bool IsStorageProtected(const GURL& origin) override;
   bool IsStorageUnlimited(const GURL& origin) override;
   bool IsStorageSessionOnly(const GURL& origin) override;
-  bool CanQueryDiskSize(const GURL& origin) override;
   bool HasSessionOnlyOrigins() override;
 
   void AddProtected(const GURL& origin) {
diff --git a/chrome/browser/recovery/recovery_install_global_error.cc b/chrome/browser/recovery/recovery_install_global_error.cc
index 8e2964a..0b4d55b 100644
--- a/chrome/browser/recovery/recovery_install_global_error.cc
+++ b/chrome/browser/recovery/recovery_install_global_error.cc
@@ -14,10 +14,11 @@
 #include "chrome/common/chrome_switches.h"
 #include "chrome/common/pref_names.h"
 #include "chrome/grit/chromium_strings.h"
-#include "chrome/grit/theme_resources.h"
 #include "components/prefs/pref_service.h"
 #include "ui/base/l10n/l10n_util.h"
-#include "ui/base/resource/resource_bundle.h"
+#include "ui/gfx/paint_vector_icon.h"
+#include "ui/gfx/vector_icons_public.h"
+#include "ui/native_theme/native_theme.h"
 
 RecoveryInstallGlobalError::RecoveryInstallGlobalError(Profile* profile)
         : elevation_needed_(false),
@@ -43,8 +44,7 @@
                  base::Unretained(this)));
 }
 
-RecoveryInstallGlobalError::~RecoveryInstallGlobalError() {
-}
+RecoveryInstallGlobalError::~RecoveryInstallGlobalError() {}
 
 void RecoveryInstallGlobalError::Shutdown() {
   GlobalErrorServiceFactory::GetForProfile(profile_)->RemoveUnownedGlobalError(
@@ -68,8 +68,10 @@
 }
 
 gfx::Image RecoveryInstallGlobalError::MenuItemIcon() {
-  return ResourceBundle::GetSharedInstance().GetNativeImageNamed(
-      IDR_UPDATE_MENU_SEVERITY_HIGH);
+  return gfx::Image(gfx::CreateVectorIcon(
+      gfx::VectorIconId::BROWSER_TOOLS_UPDATE,
+      ui::NativeTheme::GetInstanceForNativeUi()->GetSystemColor(
+          ui::NativeTheme::kColorId_AlertSeverityHigh)));
 }
 
 void RecoveryInstallGlobalError::ExecuteMenuItem(Browser* browser) {
@@ -94,8 +96,9 @@
 }
 
 gfx::Image RecoveryInstallGlobalError::GetBubbleViewIcon() {
-  return ResourceBundle::GetSharedInstance().GetNativeImageNamed(
-      IDR_UPDATE_MENU_SEVERITY_HIGH);
+  // TODO(estade): there shouldn't be an icon in the bubble, but
+  // GlobalErrorBubbleView currently requires it. See crbug.com/673995
+  return MenuItemIcon();
 }
 
 base::string16 RecoveryInstallGlobalError::GetBubbleViewTitle() {
diff --git a/chrome/browser/resources/settings/advanced_page/advanced_page.html b/chrome/browser/resources/settings/advanced_page/advanced_page.html
deleted file mode 100644
index 45db38c..0000000
--- a/chrome/browser/resources/settings/advanced_page/advanced_page.html
+++ /dev/null
@@ -1,104 +0,0 @@
-<link rel="import" href="chrome://resources/html/polymer.html">
-<link rel="import" href="/a11y_page/a11y_page.html">
-<link rel="import" href="/downloads_page/downloads_page.html">
-<link rel="import" href="/languages_page/languages_page.html">
-<link rel="import" href="/passwords_and_forms_page/passwords_and_forms_page.html">
-<link rel="import" href="/printing_page/printing_page.html">
-<link rel="import" href="/privacy_page/privacy_page.html">
-<link rel="import" href="/reset_page/reset_page.html">
-<link rel="import" href="/settings_page/main_page_behavior.html">
-<link rel="import" href="/settings_page/settings_page_visibility.html">
-<link rel="import" href="/settings_page/settings_section.html">
-<link rel="import" href="/settings_page_css.html">
-
-<if expr="chromeos">
-<link rel="import" href="/bluetooth_page/bluetooth_page.html">
-<link rel="import" href="/date_time_page/date_time_page.html">
-</if>
-
-<if expr="not chromeos">
-<link rel="import" href="/system_page/system_page.html">
-</if>
-
-<dom-module id="settings-advanced-page">
-  <template>
-    <style include="settings-page-styles"></style>
-    <div>
-<if expr="chromeos">
-      <template is="dom-if" if="[[showPage(pageVisibility.dateTime)]]" restamp>
-        <settings-section page-title="$i18n{dateTimePageTitle}"
-            section="dateTime">
-          <settings-date-time-page prefs="{{prefs}}"
-              page-visibility="[[pageVisibility.dateTime]]">
-          </settings-date-time-page>
-        </settings-section>
-      </template>
-</if>
-      <template is="dom-if" if="[[showPage(pageVisibility.privacy)]]" restamp>
-        <settings-section page-title="$i18n{privacyPageTitle}"
-            section="privacy">
-          <settings-privacy-page prefs="{{prefs}}"
-              page-visibility="[[pageVisibility.privacy]]">
-          </settings-privacy-page>
-        </settings-section>
-      </template>
-<if expr="chromeos">
-      <template is="dom-if" if="[[showPage(pageVisibility.bluetooth)]]" restamp>
-        <settings-section page-title="$i18n{bluetoothPageTitle}"
-            section="bluetooth">
-          <settings-bluetooth-page prefs="{{prefs}}"></settings-bluetooth-page>
-        </settings-section>
-      </template>
-</if>
-      <template is="dom-if" if="[[showPage(pageVisibility.passwordsAndForms)]]"
-          restamp>
-        <settings-section
-            page-title="$i18n{passwordsAndAutofillPageTitle}"
-            section="passwordsAndForms">
-          <settings-passwords-and-forms-page prefs="{{prefs}}">
-          </settings-passwords-and-forms-page>
-        </settings-section>
-      </template>
-      <template is="dom-if" if="[[showPage(pageVisibility.languages)]]" restamp>
-        <settings-section page-title="$i18n{languagesPageTitle}"
-            section="languages">
-          <settings-languages-page prefs="{{prefs}}"></settings-languages-page>
-        </settings-section>
-      </template>
-      <template is="dom-if" if="[[showPage(pageVisibility.downloads)]]" restamp>
-        <settings-section page-title="$i18n{downloadsPageTitle}"
-            section="downloads">
-          <settings-downloads-page prefs="{{prefs}}"
-              page-visibility="[[pageVisibility.downloads]]">
-          </settings-downloads-page>
-        </settings-section>
-      </template>
-      <template is="dom-if" if="[[showPage(pageVisibility.printing)]]" restamp>
-        <settings-section page-title="$i18n{printingPageTitle}"
-            section="printing">
-          <settings-printing-page prefs="{{prefs}}"></settings-printing-page>
-        </settings-section>
-      </template>
-      <template is="dom-if" if="[[showPage(pageVisibility.a11y)]]" restamp>
-        <settings-section page-title="$i18n{a11yPageTitle}" section="a11y">
-          <settings-a11y-page prefs="{{prefs}}"
-              current-route="{{currentRoute}}">
-          </settings-a11y-page>
-        </settings-section>
-      </template>
-<if expr="not chromeos">
-      <template is="dom-if" if="[[showPage(pageVisibility.system)]]" restamp>
-        <settings-section page-title="$i18n{systemPageTitle}" section="system">
-          <settings-system-page prefs="{{prefs}}"></settings-system-page>
-        </settings-section>
-      </template>
-</if>
-      <template is="dom-if" if="[[showPage(pageVisibility.reset)]]" restamp>
-        <settings-section page-title="$i18n{resetPageTitle}" section="reset">
-          <settings-reset-page></settings-reset-page>
-        </settings-section>
-      </template>
-    </div>
-  </template>
-  <script src="/advanced_page/advanced_page.js"></script>
-</dom-module>
diff --git a/chrome/browser/resources/settings/advanced_page/advanced_page.js b/chrome/browser/resources/settings/advanced_page/advanced_page.js
deleted file mode 100644
index 336b5f4..0000000
--- a/chrome/browser/resources/settings/advanced_page/advanced_page.js
+++ /dev/null
@@ -1,22 +0,0 @@
-// Copyright 2015 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-/**
- * @fileoverview
- * 'settings-advanced-page' is the settings page containing the advanced
- * settings.
- */
-Polymer({
-  is: 'settings-advanced-page',
-
-  behaviors: [SettingsPageVisibility, MainPageBehavior],
-
-  properties: {
-    /** Preferences state. */
-    prefs: {
-      type: Object,
-      notify: true,
-    },
-  },
-});
diff --git a/chrome/browser/resources/settings/advanced_page/compiled_resources2.gyp b/chrome/browser/resources/settings/advanced_page/compiled_resources2.gyp
deleted file mode 100644
index 36f8252..0000000
--- a/chrome/browser/resources/settings/advanced_page/compiled_resources2.gyp
+++ /dev/null
@@ -1,17 +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.
-{
-  'targets': [
-    {
-      'target_name': 'advanced_page',
-      'dependencies': [
-        '../compiled_resources2.gyp:route',
-        '../settings_page/compiled_resources2.gyp:main_page_behavior',
-        '../settings_page/compiled_resources2.gyp:settings_page_visibility',
-        '../system_page/compiled_resources2.gyp:system_page',
-      ],
-      'includes': ['../../../../../third_party/closure_compiler/compile_js2.gypi'],
-    },
-  ],
-}
diff --git a/chrome/browser/resources/settings/basic_page/basic_page.html b/chrome/browser/resources/settings/basic_page/basic_page.html
index 73b5e03..817b967d 100644
--- a/chrome/browser/resources/settings/basic_page/basic_page.html
+++ b/chrome/browser/resources/settings/basic_page/basic_page.html
@@ -1,94 +1,252 @@
 <link rel="import" href="chrome://resources/html/polymer.html">
+<link rel="import" href="/a11y_page/a11y_page.html">
 <link rel="import" href="/appearance_page/appearance_page.html">
+<link rel="import" href="/downloads_page/downloads_page.html">
+<link rel="import" href="/languages_page/languages_page.html">
+<link rel="import" href="/on_startup_page/on_startup_page.html">
+<link rel="import" href="/passwords_and_forms_page/passwords_and_forms_page.html">
+<link rel="import" href="/people_page/people_page.html">
+<link rel="import" href="/printing_page/printing_page.html">
+<link rel="import" href="/privacy_page/privacy_page.html">
+<link rel="import" href="/reset_page/reset_page.html">
+<link rel="import" href="/reset_page/reset_profile_banner.html">
 <link rel="import" href="/search_page/search_page.html">
 <link rel="import" href="/settings_page/main_page_behavior.html">
 <link rel="import" href="/settings_page/settings_page_visibility.html">
 <link rel="import" href="/settings_page/settings_section.html">
-<link rel="import" href="/on_startup_page/on_startup_page.html">
-<link rel="import" href="/people_page/people_page.html">
-<link rel="import" href="/reset_page/reset_profile_banner.html">
 <link rel="import" href="/settings_page_css.html">
+<link rel="import" href="/settings_vars_css.html">
 
 <if expr="chromeos">
 <link rel="import" href="/android_apps_page/android_apps_page.html">
+<link rel="import" href="/bluetooth_page/bluetooth_page.html">
+<link rel="import" href="/date_time_page/date_time_page.html">
 <link rel="import" href="/device_page/device_page.html">
 <link rel="import" href="/internet_page/internet_page.html">
 </if>
 
 <if expr="not chromeos">
 <link rel="import" href="/default_browser_page/default_browser_page.html">
+<link rel="import" href="/system_page/system_page.html">
 </if>
 
+<!-- TODO(michaelpg): Rename to something better than "basic" now that this page
+     includes both the basic and advanced settings. -->
 <dom-module id="settings-basic-page">
   <template>
-    <style include="settings-page-styles"></style>
-    <div>
-      <template is="dom-if" if="[[showResetProfileBanner_]]">
-        <settings-reset-profile-banner on-reset-done="onResetDone_">
-        </settings-reset-profile-banner>
-      </template>
+    <style include="settings-page-styles">
+      #advancedToggle {
+        --paper-button: {
+          text-transform: none;
+        }
+        @apply(--settings-actionable);
+        align-items: center;
+        display: flex;
+        margin-bottom: 3px;
+        margin-top: 12px;  /* Part of a 48px spacer (33px + 12px + 3px). */
+        min-height: 32px;
+        padding: 0 12px;
+      }
+
+      #toggleContainer {
+        align-items: center;
+        display: flex;
+        font: inherit;
+        justify-content: center;
+        margin-bottom: 0;
+        margin-top: 0;
+      }
+
+      #toggleSpacer {
+        padding-top: 33px;  /* Part of a 48px spacer (33px + 12px + 3px). */
+      }
+
+      iron-icon {
+        -webkit-margin-start: 16px;
+      }
+    </style>
+    <template is="dom-if" if="[[showBasicPage_(
+        currentRoute_, inSearchMode, hasExpandedSection_)]]">
+      <div id="basicPage">
+        <template is="dom-if" if="[[showResetProfileBanner_]]">
+          <settings-reset-profile-banner on-reset-done="onResetDone_">
+          </settings-reset-profile-banner>
+        </template>
 <if expr="chromeos">
-      <template is="dom-if" if="[[showPage(pageVisibility.internet)]]" restamp>
-        <settings-section page-title="$i18n{internetPageTitle}"
-            section="internet">
-          <settings-internet-page prefs="{{prefs}}">
-          </settings-internet-page>
-        </settings-section>
-      </template>
+        <template is="dom-if" if="[[showPage(pageVisibility.internet)]]"
+            restamp>
+          <settings-section page-title="$i18n{internetPageTitle}"
+              section="internet">
+            <settings-internet-page prefs="{{prefs}}">
+            </settings-internet-page>
+          </settings-section>
+        </template>
 </if>
-      <template is="dom-if" if="[[showPage(pageVisibility.people)]]" restamp>
-        <settings-section page-title="$i18n{peoplePageTitle}" section="people">
-          <settings-people-page prefs="{{prefs}}"></settings-people-page>
-        </settings-section>
-      </template>
-      <template is="dom-if" if="[[showPage(pageVisibility.appearance)]]"
-          restamp>
-        <settings-section page-title="$i18n{appearancePageTitle}"
-            section="appearance">
-          <settings-appearance-page prefs="{{prefs}}"
-              page-visibility="[[pageVisibility.appearance]]">
-          </settings-appearance-page>
-        </settings-section>
-      </template>
+        <template is="dom-if" if="[[showPage(pageVisibility.people)]]" restamp>
+          <settings-section page-title="$i18n{peoplePageTitle}"
+              section="people">
+            <settings-people-page prefs="{{prefs}}"></settings-people-page>
+          </settings-section>
+        </template>
+        <template is="dom-if" if="[[showPage(pageVisibility.appearance)]]"
+            restamp>
+          <settings-section page-title="$i18n{appearancePageTitle}"
+              section="appearance">
+            <settings-appearance-page prefs="{{prefs}}"
+                page-visibility="[[pageVisibility.appearance]]">
+            </settings-appearance-page>
+          </settings-section>
+        </template>
 <if expr="chromeos">
-      <template is="dom-if" if="[[showPage(pageVisibility.device)]]" restamp>
-        <settings-section page-title="$i18n{devicePageTitle}" section="device">
-          <settings-device-page prefs="{{prefs}}"></settings-device-page>
-        </settings-section>
-      </template>
+        <template is="dom-if" if="[[showPage(pageVisibility.device)]]" restamp>
+          <settings-section page-title="$i18n{devicePageTitle}"
+              section="device">
+            <settings-device-page prefs="{{prefs}}"></settings-device-page>
+          </settings-section>
+        </template>
 </if>
-      <template is="dom-if" if="[[showPage(pageVisibility.search)]]" restamp>
-        <settings-section page-title="$i18n{searchPageTitle}" section="search">
-          <settings-search-page prefs="[[prefs]]"></settings-search-page>
-        </settings-section>
-      </template>
+        <template is="dom-if" if="[[showPage(pageVisibility.search)]]" restamp>
+          <settings-section page-title="$i18n{searchPageTitle}" section="search">
+            <settings-search-page prefs="[[prefs]]"></settings-search-page>
+          </settings-section>
+        </template>
 <if expr="chromeos">
-      <template is="dom-if"
-          if="[[shouldShowAndroidApps_(showAndroidApps, pageVisibility)]]"
-          restamp>
-        <settings-section page-title="$i18n{androidAppsPageTitle}"
-            section="androidApps">
-          <settings-android-apps-page prefs="{{prefs}}">
-          </settings-android-apps-page>
-        </settings-section>
-      </template>
+        <template is="dom-if"
+            if="[[shouldShowAndroidApps_(showAndroidApps, pageVisibility)]]"
+            restamp>
+          <settings-section page-title="$i18n{androidAppsPageTitle}"
+              section="androidApps">
+            <settings-android-apps-page prefs="{{prefs}}">
+            </settings-android-apps-page>
+          </settings-section>
+        </template>
 </if>
 <if expr="not chromeos">
-      <template is="dom-if" if="[[showPage(pageVisibility.defaultBrowser)]]"
-          restamp>
-        <settings-section page-title="$i18n{defaultBrowser}"
-            section="defaultBrowser">
-          <settings-default-browser-page></settings-default-browser-page>
-        </settings-section>
-      </template>
+        <template is="dom-if" if="[[showPage(pageVisibility.defaultBrowser)]]"
+            restamp>
+          <settings-section page-title="$i18n{defaultBrowser}"
+              section="defaultBrowser">
+            <settings-default-browser-page></settings-default-browser-page>
+          </settings-section>
+        </template>
 </if>
-      <template is="dom-if" if="[[showPage(pageVisibility.onStartup)]]" restamp>
-        <settings-section page-title="$i18n{onStartup}" section="onStartup">
-          <settings-on-startup-page prefs="{{prefs}}">
-          </settings-on-startup-page>
-        </settings-section>
+        <template is="dom-if" if="[[showPage(pageVisibility.onStartup)]]"
+            restamp>
+          <settings-section page-title="$i18n{onStartup}" section="onStartup">
+            <settings-on-startup-page prefs="{{prefs}}">
+            </settings-on-startup-page>
+          </settings-section>
+        </template>
+      </div>
+    </template>
+
+    <template is="dom-if"
+        if="[[showAdvancedSettings_(pageVisibility.advancedSettings)]]">
+      <template is="dom-if" if="[[showAdvancedToggle_(
+          inSearchMode, hasExpandedSection_)]]">
+        <div id="toggleSpacer"></div>
+        <h2 id="toggleContainer">
+          <paper-button id="advancedToggle" active="{{advancedToggleExpanded}}"
+              aria-active-attribute="aria-expanded" toggles>
+            <span>$i18n{advancedPageTitle}</span>
+            <iron-icon icon="[[getArrowIcon_(advancedToggleExpanded)]]">
+            </iron-icon>
+          </paper-button>
+        </h2>
       </template>
-    </div>
+
+      <template is="dom-if" if="[[showAdvancedPage_(
+          currentRoute_, inSearchMode, hasExpandedSection_,
+          advancedToggleExpanded)]]">
+        <div id="advancedPage">
+<if expr="chromeos">
+          <template is="dom-if" if="[[showPage(pageVisibility.dateTime)]]"
+              restamp>
+            <settings-section page-title="$i18n{dateTimePageTitle}"
+                section="dateTime">
+              <settings-date-time-page prefs="{{prefs}}"
+                  page-visibility="[[pageVisibility.dateTime]]">
+              </settings-date-time-page>
+            </settings-section>
+          </template>
+</if>
+          <template is="dom-if" if="[[showPage(pageVisibility.privacy)]]"
+              restamp>
+            <settings-section page-title="$i18n{privacyPageTitle}"
+                section="privacy">
+              <settings-privacy-page prefs="{{prefs}}"
+                  page-visibility="[[pageVisibility.privacy]]">
+              </settings-privacy-page>
+            </settings-section>
+          </template>
+<if expr="chromeos">
+          <template is="dom-if" if="[[showPage(pageVisibility.bluetooth)]]"
+              restamp>
+            <settings-section page-title="$i18n{bluetoothPageTitle}"
+                section="bluetooth">
+              <settings-bluetooth-page prefs="{{prefs}}">
+              </settings-bluetooth-page>
+            </settings-section>
+          </template>
+</if>
+          <template is="dom-if"
+              if="[[showPage(pageVisibility.passwordsAndForms)]]" restamp>
+            <settings-section
+                page-title="$i18n{passwordsAndAutofillPageTitle}"
+                section="passwordsAndForms">
+              <settings-passwords-and-forms-page prefs="{{prefs}}">
+              </settings-passwords-and-forms-page>
+            </settings-section>
+          </template>
+          <template is="dom-if" if="[[showPage(pageVisibility.languages)]]"
+              restamp>
+            <settings-section page-title="$i18n{languagesPageTitle}"
+                section="languages">
+              <settings-languages-page prefs="{{prefs}}">
+              </settings-languages-page>
+            </settings-section>
+          </template>
+          <template is="dom-if" if="[[showPage(pageVisibility.downloads)]]"
+              restamp>
+            <settings-section page-title="$i18n{downloadsPageTitle}"
+                section="downloads">
+              <settings-downloads-page prefs="{{prefs}}"
+                  page-visibility="[[pageVisibility.downloads]]">
+              </settings-downloads-page>
+            </settings-section>
+          </template>
+          <template is="dom-if" if="[[showPage(pageVisibility.printing)]]"
+              restamp>
+            <settings-section page-title="$i18n{printingPageTitle}"
+                section="printing">
+              <settings-printing-page prefs="{{prefs}}">
+              </settings-printing-page>
+            </settings-section>
+          </template>
+          <template is="dom-if" if="[[showPage(pageVisibility.a11y)]]" restamp>
+            <settings-section page-title="$i18n{a11yPageTitle}" section="a11y">
+              <settings-a11y-page prefs="{{prefs}}"></settings-a11y-page>
+            </settings-section>
+          </template>
+<if expr="not chromeos">
+          <template is="dom-if" if="[[showPage(pageVisibility.system)]]"
+              restamp>
+            <settings-section page-title="$i18n{systemPageTitle}"
+                section="system">
+              <settings-system-page prefs="{{prefs}}"></settings-system-page>
+            </settings-section>
+          </template>
+</if>
+          <template is="dom-if" if="[[showPage(pageVisibility.reset)]]"
+              restamp>
+            <settings-section page-title="$i18n{resetPageTitle}"
+                section="reset">
+              <settings-reset-page></settings-reset-page>
+            </settings-section>
+          </template>
+        </div>
+      </template>
+    </template>
   </template>
   <script src="basic_page.js"></script>
 </dom-module>
diff --git a/chrome/browser/resources/settings/basic_page/basic_page.js b/chrome/browser/resources/settings/basic_page/basic_page.js
index 873aa2d..dde5ade 100644
--- a/chrome/browser/resources/settings/basic_page/basic_page.js
+++ b/chrome/browser/resources/settings/basic_page/basic_page.js
@@ -4,7 +4,7 @@
 
 /**
  * @fileoverview
- * 'settings-basic-page' is the settings page containing the basic settings.
+ * 'settings-basic-page' is the settings page containing the actual settings.
  */
 Polymer({
   is: 'settings-basic-page',
@@ -21,6 +21,27 @@
     showAndroidApps: Boolean,
 
     /**
+     * Dictionary defining page visibility.
+     * @type {!GuestModePageVisibility}
+     */
+    pageVisibility: Object,
+
+    advancedToggleExpanded: {
+      type: Boolean,
+      notify: true,
+    },
+
+    /**
+     * True if a section is fully expanded to hide other sections beneath it.
+     * False otherwise (even while animating a section open/closed).
+     * @private {boolean}
+     */
+    hasExpandedSection_: {
+      type: Boolean,
+      value: false,
+    },
+
+    /**
      * True if the basic page should currently display the reset profile banner.
      * @private {boolean}
      */
@@ -30,6 +51,60 @@
         return loadTimeData.getBoolean('showResetProfileBanner');
       },
     },
+
+    /** @private {!settings.Route|undefined} */
+    currentRoute_: Object,
+  },
+
+  listeners: {
+    'subpage-expand': 'onSubpageExpanded_',
+  },
+
+  /** @override */
+  attached: function() {
+    this.currentRoute_ = settings.getCurrentRoute();
+  },
+
+  /**
+   * Overrides MainPageBehaviorImpl from MainPageBehavior.
+   * @param {!settings.Route} newRoute
+   * @param {settings.Route} oldRoute
+   */
+  currentRouteChanged: function(newRoute, oldRoute) {
+    this.currentRoute_ = newRoute;
+
+    if (settings.Route.ADVANCED.contains(newRoute))
+      this.advancedToggleExpanded = true;
+
+    if (oldRoute && oldRoute.isSubpage()) {
+      // If the new route isn't the same expanded section, reset
+      // hasExpandedSection_ for the next transition.
+      if (!newRoute.isSubpage() || newRoute.section != oldRoute.section)
+        this.hasExpandedSection_ = false;
+    } else {
+      assert(!this.hasExpandedSection_);
+    }
+
+    MainPageBehaviorImpl.currentRouteChanged.call(this, newRoute, oldRoute);
+  },
+
+  /**
+   * Queues a task to search the basic sections, then another for the advanced
+   * sections.
+   * @param {string} query The text to search for.
+   * @return {!Promise<!settings.SearchRequest>} A signal indicating that
+   *     searching finished.
+   */
+  searchContents: function(query) {
+    var whenSearchDone = settings.getSearchManager().search(
+        query, assert(this.$$('#basicPage')));
+
+    if (this.pageVisibility.advancedSettings !== false) {
+      assert(whenSearchDone === settings.getSearchManager().search(
+          query, assert(this.$$('#advancedPage'))));
+    }
+
+    return whenSearchDone;
   },
 
   /** @private */
@@ -46,4 +121,68 @@
         this.get('pageVisibility.androidApps'));
     return this.showAndroidApps && this.showPage(visibility);
   },
+
+  /**
+   * Hides everything but the newly expanded subpage.
+   * @private
+   */
+  onSubpageExpanded_: function() {
+    this.hasExpandedSection_ = true;
+  },
+
+  /**
+   * @param {boolean} inSearchMode
+   * @param {boolean} hasExpandedSection
+   * @return {boolean}
+   * @private
+   */
+  showAdvancedToggle_: function(inSearchMode, hasExpandedSection) {
+    return !inSearchMode && !hasExpandedSection;
+  },
+
+  /**
+   * @param {!settings.Route} currentRoute
+   * @param {boolean} inSearchMode
+   * @param {boolean} hasExpandedSection
+   * @return {boolean} Whether to show the basic page, taking into account
+   *     both routing and search state.
+   * @private
+   */
+  showBasicPage_: function(currentRoute, inSearchMode, hasExpandedSection) {
+    return !hasExpandedSection || settings.Route.BASIC.contains(currentRoute);
+  },
+
+  /**
+   * @param {!settings.Route} currentRoute
+   * @param {boolean} inSearchMode
+   * @param {boolean} hasExpandedSection
+   * @param {boolean} advancedToggleExpanded
+   * @return {boolean} Whether to show the advanced page, taking into account
+   *     both routing and search state.
+   * @private
+   */
+  showAdvancedPage_: function(currentRoute, inSearchMode, hasExpandedSection,
+                              advancedToggleExpanded) {
+    return hasExpandedSection ?
+        settings.Route.ADVANCED.contains(currentRoute) :
+        advancedToggleExpanded || inSearchMode;
+  },
+
+  /**
+   * @param {(boolean|undefined)} visibility
+   * @return {boolean} True unless visibility is false.
+   * @private
+   */
+  showAdvancedSettings_: function(visibility) {
+    return visibility !== false;
+  },
+
+  /**
+   * @param {boolean} opened Whether the menu is expanded.
+   * @return {string} Icon name.
+   * @private
+   */
+  getArrowIcon_: function(opened) {
+    return opened ? 'settings:arrow-drop-up' : 'cr:arrow-drop-down';
+  },
 });
diff --git a/chrome/browser/resources/settings/basic_page/compiled_resources2.gyp b/chrome/browser/resources/settings/basic_page/compiled_resources2.gyp
index c5fc14019..7a9b9ff0 100644
--- a/chrome/browser/resources/settings/basic_page/compiled_resources2.gyp
+++ b/chrome/browser/resources/settings/basic_page/compiled_resources2.gyp
@@ -8,8 +8,10 @@
       'dependencies': [
         '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:load_time_data',
         '../compiled_resources2.gyp:route',
+        '../compiled_resources2.gyp:search_settings',
         '../settings_page/compiled_resources2.gyp:main_page_behavior',
         '../settings_page/compiled_resources2.gyp:settings_page_visibility',
+        '../settings_ui/compiled_resources2.gyp:settings_ui_types',
       ],
       'includes': ['../../../../../third_party/closure_compiler/compile_js2.gypi'],
     },
diff --git a/chrome/browser/resources/settings/compiled_resources2.gyp b/chrome/browser/resources/settings/compiled_resources2.gyp
index 74bb172d..c342b40 100644
--- a/chrome/browser/resources/settings/compiled_resources2.gyp
+++ b/chrome/browser/resources/settings/compiled_resources2.gyp
@@ -57,7 +57,6 @@
       'dependencies': [
         'a11y_page/compiled_resources2.gyp:*',
         'about_page/compiled_resources2.gyp:*',
-        'advanced_page/compiled_resources2.gyp:*',
         'android_apps_page/compiled_resources2.gyp:*',
         'animation/compiled_resources2.gyp:*',
         'appearance_page/compiled_resources2.gyp:*',
diff --git a/chrome/browser/resources/settings/search_settings.js b/chrome/browser/resources/settings/search_settings.js
index d349f922..942d436 100644
--- a/chrome/browser/resources/settings/search_settings.js
+++ b/chrome/browser/resources/settings/search_settings.js
@@ -362,8 +362,8 @@
      * @private
      */
     setSectionsVisibility_: function(visible) {
-      var sections = Polymer.dom(
-          this.node.root).querySelectorAll('settings-section');
+      var sections = this.node.querySelectorAll('settings-section');
+
       for (var i = 0; i < sections.length; i++)
         sections[i].hiddenBySearch = !visible;
     },
diff --git a/chrome/browser/resources/settings/settings_main/compiled_resources2.gyp b/chrome/browser/resources/settings/settings_main/compiled_resources2.gyp
index 658e968e..2efdb052 100644
--- a/chrome/browser/resources/settings/settings_main/compiled_resources2.gyp
+++ b/chrome/browser/resources/settings/settings_main/compiled_resources2.gyp
@@ -9,7 +9,6 @@
         '../compiled_resources2.gyp:route',
         '../compiled_resources2.gyp:search_settings',
         '../about_page/compiled_resources2.gyp:about_page',
-        '../advanced_page/compiled_resources2.gyp:advanced_page',
         '../basic_page/compiled_resources2.gyp:basic_page',
         '../settings_page/compiled_resources2.gyp:main_page_behavior',
         '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:assert',
diff --git a/chrome/browser/resources/settings/settings_main/settings_main.html b/chrome/browser/resources/settings/settings_main/settings_main.html
index 3a336106..3023de7 100644
--- a/chrome/browser/resources/settings/settings_main/settings_main.html
+++ b/chrome/browser/resources/settings/settings_main/settings_main.html
@@ -4,49 +4,17 @@
 <link rel="import" href="chrome://resources/polymer/v1_0/iron-icon/iron-icon.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/paper-button/paper-button.html">
 <link rel="import" href="/about_page/about_page.html">
-<link rel="import" href="/advanced_page/advanced_page.html">
 <link rel="import" href="/basic_page/basic_page.html">
 <link rel="import" href="/route.html">
-<link rel="import" href="/settings_page/main_page_behavior.html">
 <link rel="import" href="/settings_vars_css.html">
 
 <dom-module id="settings-main">
   <template>
     <style>
-      #advancedToggle {
-        --paper-button: {
-          text-transform: none;
-        }
-        @apply(--settings-actionable);
-        align-items: center;
-        display: flex;
-        margin-bottom: 3px;
-        margin-top: 12px;  /* Part of a 48px spacer (33px + 12px + 3px). */
-        min-height: 32px;
-        padding: 0 12px;
-      }
-
       #overscroll {
         margin-top: 64px;
       }
 
-      #toggleContainer {
-        align-items: center;
-        display: flex;
-        font: inherit;
-        justify-content: center;
-        margin-bottom: 0;
-        margin-top: 0;
-      }
-
-      #toggleSpacer {
-        padding-top: 33px;  /* Part of a 48px spacer (33px + 12px + 3px). */
-      }
-
-      iron-icon {
-        -webkit-margin-start: 16px;
-      }
-
       #noSearchResults {
         align-items: center;
         display: flex;
@@ -64,38 +32,15 @@
       <div>$i18n{searchNoResults}</div>
       <div>$i18nRaw{searchNoResultsHelp}</div>
     </div>
-    <template is="dom-if" if="[[showBasicPage_(
-        showPages_.basic, inSearchMode_, hasExpandedSection_)]]">
+    <template is="dom-if" if="[[showPages_.settings]]">
       <settings-basic-page prefs="{{prefs}}"
           page-visibility="[[pageVisibility]]"
           show-android-apps="[[showAndroidApps]]"
           on-subpage-expand="onSubpageExpand_"
-          in-search-mode="[[inSearchMode_]]">
+          in-search-mode="[[inSearchMode_]]"
+          advanced-toggle-expanded="{{advancedToggleExpanded}}">
       </settings-basic-page>
     </template>
-    <template is="dom-if"
-        if="[[showAdvancedSettings_(pageVisibility.advancedSettings)]]">
-      <template is="dom-if" if="[[showAdvancedToggle_(
-          showPages_.basic, hasExpandedSection_, inSearchMode_)]]">
-        <div id="toggleSpacer"></div>
-        <h2 id="toggleContainer">
-          <paper-button id="advancedToggle" active="{{advancedToggleExpanded}}"
-              aria-active-attribute="aria-expanded" toggles>
-            <span>$i18n{advancedPageTitle}</span>
-            <iron-icon icon="[[arrowState_(advancedToggleExpanded)]]">
-            </iron-icon>
-          </paper-button>
-        </h2>
-      </template>
-      <template is="dom-if" if="[[showAdvancedPage_(
-          showPages_.advanced, inSearchMode_, hasExpandedSection_)]]">
-        <settings-advanced-page prefs="{{prefs}}"
-            page-visibility="[[pageVisibility]]"
-            on-subpage-expand="onSubpageExpand_"
-            in-search-mode="[[inSearchMode_]]">
-        </settings-advanced-page>
-      </template>
-    </template>
     <template is="dom-if" if="[[showPages_.about]]">
       <settings-about-page in-search-mode="[[inSearchMode_]]">
       </settings-about-page>
diff --git a/chrome/browser/resources/settings/settings_main/settings_main.js b/chrome/browser/resources/settings/settings_main/settings_main.js
index b5f524a..51bb5f4 100644
--- a/chrome/browser/resources/settings/settings_main/settings_main.js
+++ b/chrome/browser/resources/settings/settings_main/settings_main.js
@@ -3,7 +3,7 @@
 // found in the LICENSE file.
 
 /**
- * @typedef {{about: boolean, basic: boolean, advanced: boolean}}
+ * @typedef {{about: boolean, settings: boolean}}
  */
 var MainPageVisibility;
 
@@ -28,16 +28,8 @@
     advancedToggleExpanded: {
       type: Boolean,
       notify: true,
-      observer: 'updatePagesShown_',
     },
 
-    /**
-     * True if a section is fully expanded to hide other sections beneath it.
-     * Not true otherwise (even while animating a section open/closed).
-     * @private
-     */
-    hasExpandedSection_: Boolean,
-
     /** @private */
     overscroll_: {
       type: Number,
@@ -46,13 +38,13 @@
 
     /**
      * Controls which main pages are displayed via dom-ifs, based on the current
-     * route and the Advanced toggle state.
+     * route.
      * @private {!MainPageVisibility}
      */
     showPages_: {
       type: Object,
       value: function() {
-        return {about: false, basic: false, advanced: false};
+        return {about: false, settings: false};
       },
     },
 
@@ -93,8 +85,6 @@
   /** @override */
   attached: function() {
     this.listen(this, 'freeze-scroll', 'onFreezeScroll_');
-    var currentRoute = settings.getCurrentRoute();
-    this.hasExpandedSection_ = currentRoute && currentRoute.isSubpage();
   },
 
   /** @override */
@@ -162,84 +152,24 @@
     }
   },
 
-  /**
-   * @param {boolean} opened Whether the menu is expanded.
-   * @return {string} Which icon to use.
-   * @private
-   */
-  arrowState_: function(opened) {
-    return opened ? 'settings:arrow-drop-up' : 'cr:arrow-drop-down';
-  },
-
-  /**
-   * @return {boolean}
-   * @private
-   */
-  showAdvancedToggle_: function() {
-    return !this.inSearchMode_ && this.showPages_.basic &&
-        !this.hasExpandedSection_;
-  },
-
-  /**
-   * @return {boolean} Whether to show the basic page, taking into account both
-   *     routing and search state.
-   * @private
-   */
-  showBasicPage_: function() {
-    return this.showPages_.basic || (
-        this.inSearchMode_ && !this.hasExpandedSection_);
-  },
-
-  /**
-   * @return {boolean} Whether to show the advanced page, taking into account
-   *     both routing and search state.
-   * @private
-   */
-  showAdvancedPage_: function() {
-    return this.showPages_.advanced || (
-        this.inSearchMode_ && !this.hasExpandedSection_);
-  },
-
   /** @param {!settings.Route} newRoute */
   currentRouteChanged: function(newRoute) {
-    // When the route changes from a sub-page to the main page, immediately
-    // update hasExpandedSection_ to unhide the other sections.
-    if (!newRoute.isSubpage())
-      this.hasExpandedSection_ = false;
-
-    if (settings.Route.ADVANCED.contains(newRoute))
-      this.advancedToggleExpanded = true;
-
     this.updatePagesShown_();
   },
 
   /** @private */
   onSubpageExpand_: function() {
-    // The subpage finished expanding fully. Hide pages other than the current
-    // section's parent page.
-    this.hasExpandedSection_ = true;
     this.updatePagesShown_();
   },
 
   /**
-   * Updates the hidden state of the about, basic and advanced pages, based on
-   * the current route and the Advanced toggle state.
+   * Updates the hidden state of the about and settings pages based on the
+   * current route.
    * @private
    */
   updatePagesShown_: function() {
-    var currentRoute = settings.getCurrentRoute();
-    if (settings.Route.ABOUT.contains(currentRoute)) {
-      this.showPages_ = {about: true, basic: false, advanced: false};
-    } else {
-      this.showPages_ = {
-        about: false,
-        basic: settings.Route.BASIC.contains(currentRoute) ||
-            !this.hasExpandedSection_,
-        advanced: this.hasExpandedSection_ ?
-            settings.Route.ADVANCED.contains(currentRoute) :
-            this.advancedToggleExpanded,
-      };
-    }
+    var inAbout = settings.Route.ABOUT.contains(settings.getCurrentRoute());
+    this.showPages_ = {about: inAbout, settings: !inAbout};
 
     // Calculate and set the overflow padding.
     this.updateOverscrollForPage_();
@@ -292,27 +222,18 @@
     return Math.max(0, this.offsetParent.clientHeight - distance);
   },
 
-  /** @private */
-  toggleAdvancedPage_: function() {
-    this.advancedToggleExpanded = !this.advancedToggleExpanded;
-  },
-
   /**
    * Returns the root page (if it exists) for a route.
    * @param {!settings.Route} route
-   * @return {(?SettingsAboutPageElement|?SettingsAdvancedPageElement|
-   *           ?SettingsBasicPageElement)}
+   * @return {(?SettingsAboutPageElement|?SettingsBasicPageElement)}
    */
   getPage_: function(route) {
     if (settings.Route.ABOUT.contains(route)) {
       return /** @type {?SettingsAboutPageElement} */(
           this.$$('settings-about-page'));
     }
-    if (settings.Route.ADVANCED.contains(route)) {
-      return /** @type {?SettingsAdvancedPageElement} */(
-          this.$$('settings-advanced-page'));
-    }
-    if (settings.Route.BASIC.contains(route)) {
+    if (settings.Route.BASIC.contains(route) ||
+        settings.Route.ADVANCED.contains(route)) {
       return /** @type {?SettingsBasicPageElement} */(
           this.$$('settings-basic-page'));
     }
@@ -330,14 +251,8 @@
 
     return new Promise(function(resolve, reject) {
       setTimeout(function() {
-        var whenSearchDone = settings.getSearchManager().search(
-            query, assert(this.getPage_(settings.Route.BASIC)));
-
-        if (this.pageVisibility.advancedSettings !== false) {
-          assert(whenSearchDone === settings.getSearchManager().search(
-              query, assert(this.getPage_(settings.Route.ADVANCED))));
-        }
-
+        var whenSearchDone =
+            assert(this.getPage_(settings.Route.BASIC)).searchContents(query);
         whenSearchDone.then(function(request) {
           resolve();
           if (!request.finished) {
@@ -355,13 +270,4 @@
       }.bind(this), 0);
     }.bind(this));
   },
-
-  /**
-   * @param {(boolean|undefined)} visibility
-   * @return {boolean} True unless visibility is false.
-   * @private
-   */
-  showAdvancedSettings_: function(visibility) {
-    return visibility !== false;
-  },
 });
diff --git a/chrome/browser/resources/settings/settings_page/main_page_behavior.js b/chrome/browser/resources/settings/settings_page/main_page_behavior.js
index fd15c75..0a51d833 100644
--- a/chrome/browser/resources/settings/settings_page/main_page_behavior.js
+++ b/chrome/browser/resources/settings/settings_page/main_page_behavior.js
@@ -101,9 +101,6 @@
       // If the section shouldn't be expanded, collapse it.
       if (!currentRoute.isSubpage() || expandedSection != currentSection) {
         promise = this.collapseSection_(expandedSection);
-        // Scroll to the collapsed section.
-        if (currentSection && scrollToSection)
-          currentSection.scrollIntoView();
       } else {
         // Scroll to top while sliding to another subpage.
         this.scroller.scrollTop = 0;
@@ -253,7 +250,11 @@
         var newSection = settings.getCurrentRoute().section &&
             this.getSection(settings.getCurrentRoute().section);
 
-        this.scroller.scrollTop = this.origScrollTop_;
+        // Scroll to the new section or the original position.
+        if (newSection && !settings.lastRouteChangeWasPopstate())
+          newSection.scrollIntoView();
+        else
+          this.scroller.scrollTop = this.origScrollTop_;
 
         this.currentAnimation_ = section.animateCollapse(
             /** @type {!HTMLElement} */(this.scroller));
diff --git a/chrome/browser/resources/settings/settings_resources.grd b/chrome/browser/resources/settings/settings_resources.grd
index 9062606e..2ad4aa4 100644
--- a/chrome/browser/resources/settings/settings_resources.grd
+++ b/chrome/browser/resources/settings/settings_resources.grd
@@ -65,15 +65,6 @@
       <structure name="IDR_SETTINGS_ADD_SITE_DIALOG_JS"
                  file="site_settings/add_site_dialog.js"
                  type="chrome_html" />
-      <structure name="IDR_SETTINGS_ADVANCED_PAGE_JS"
-                 file="advanced_page/advanced_page.js"
-                 flattenhtml="true"
-                 type="chrome_html" />
-      <structure name="IDR_SETTINGS_ADVANCED_PAGE_HTML"
-                 file="advanced_page/advanced_page.html"
-                 type="chrome_html"
-                 flattenhtml="true"
-                 allowexternalscript="true" />
       <structure name="IDR_SETTINGS_ALL_SITES_HTML"
                  file="site_settings/all_sites.html"
                  type="chrome_html" />
@@ -123,6 +114,7 @@
                  flattenhtml="true" />
       <structure name="IDR_SETTINGS_BASIC_PAGE_JS"
                  file="basic_page/basic_page.js"
+                 flattenhtml="true"
                  type="chrome_html" />
       <structure name="IDR_SETTINGS_BASIC_PAGE_HTML"
                  file="basic_page/basic_page.html"
diff --git a/chrome/browser/safe_browsing/safe_browsing_navigation_observer.cc b/chrome/browser/safe_browsing/safe_browsing_navigation_observer.cc
index fea1e3b7..3efb350 100644
--- a/chrome/browser/safe_browsing/safe_browsing_navigation_observer.cc
+++ b/chrome/browser/safe_browsing/safe_browsing_navigation_observer.cc
@@ -137,7 +137,10 @@
   content::RenderFrameHost* current_frame_host =
       navigation_handle->GetWebContents()->FindFrameByFrameTreeNodeId(
           nav_event.frame_id);
-  if (current_frame_host &&
+  // For browser initiated navigation (e.g. from address bar or bookmark), we
+  // don't fill the source_url to prevent attributing navigation to the last
+  // committed navigation.
+  if (navigation_handle->IsRendererInitiated() && current_frame_host &&
       current_frame_host->GetLastCommittedURL().is_valid()) {
     nav_event.source_url = SafeBrowsingNavigationObserverManager::ClearEmptyRef(
         current_frame_host->GetLastCommittedURL());
@@ -210,17 +213,16 @@
   }
   if (!details.url.is_valid() || details.socket_address.IsEmpty())
     return;
-
-  manager_->RecordHostToIpMapping(details.url.host(),
-                                  details.socket_address.host());
+  if (!details.url.host().empty()) {
+    manager_->RecordHostToIpMapping(details.url.host(),
+                                    details.socket_address.host());
+  }
 }
 
 void SafeBrowsingNavigationObserver::DidGetUserInteraction(
     const blink::WebInputEvent::Type type) {
   last_user_gesture_timestamp_ = base::Time::Now();
   has_user_gesture_ = true;
-  // TODO (jialiul): Refine user gesture logic when DidOpenRequestedURL
-  // covers all retargetting cases.
   manager_->RecordUserGestureForWebContents(web_contents(),
                                             last_user_gesture_timestamp_);
 }
diff --git a/chrome/browser/safe_browsing/safe_browsing_navigation_observer_browsertest.cc b/chrome/browser/safe_browsing/safe_browsing_navigation_observer_browsertest.cc
index 1a09af8..7450d81 100644
--- a/chrome/browser/safe_browsing/safe_browsing_navigation_observer_browsertest.cc
+++ b/chrome/browser/safe_browsing/safe_browsing_navigation_observer_browsertest.cc
@@ -6,8 +6,10 @@
 #include "chrome/browser/chrome_notification_types.h"
 #include "chrome/browser/download/download_prefs.h"
 #include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/safe_browsing/download_protection_service.h"
 #include "chrome/browser/safe_browsing/safe_browsing_navigation_observer.h"
 #include "chrome/browser/safe_browsing/safe_browsing_navigation_observer_manager.h"
+#include "chrome/browser/safe_browsing/test_safe_browsing_service.h"
 #include "chrome/browser/sessions/session_tab_helper.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
@@ -27,6 +29,8 @@
 #include "url/gurl.h"
 #include "url/url_canon.h"
 
+using content::DownloadItem;
+
 namespace safe_browsing {
 
 const char kSingleFrameTestURL[] =
@@ -50,6 +54,18 @@
     "/safe_browsing/download_protection/navigation_observer/"
     "iframe_retargeting.html";
 const char kDownloadItemURL[] = "/safe_browsing/download_protection/signed.exe";
+const char kRedirectToLandingURL[] =
+    "/safe_browsing/download_protection/navigation_observer/"
+    "redirect_to_landing.html";
+const char kLandingURL[] =
+    "/safe_browsing/download_protection/navigation_observer/"
+    "landing.html";
+const char kLandingReferrerURL[] =
+    "/safe_browsing/download_protection/navigation_observer/"
+    "landing_referrer.html";
+const char kPageBeforeLandingReferrerURL[] =
+    "/safe_browsing/download_protection/navigation_observer/"
+    "page_before_landing_referrer.html";
 
 // Test class to help create SafeBrowsingNavigationObservers for each
 // WebContents before they are actually installed through AttachTabHelper.
@@ -93,15 +109,15 @@
                                                  false);
     ASSERT_TRUE(embedded_test_server()->Start());
     host_resolver()->AddRule("*", "127.0.0.1");
-    // Navigate to test page.
-    ui_test_utils::NavigateToURL(
-        browser(), embedded_test_server()->GetURL(kSingleFrameTestURL));
     observer_manager_ = new TestNavigationObserverManager();
     observer_ = new SafeBrowsingNavigationObserver(
         browser()->tab_strip_model()->GetActiveWebContents(),
         observer_manager_);
     ASSERT_TRUE(observer_);
     ASSERT_TRUE(InitialSetup());
+    // Navigate to test page.
+    ui_test_utils::NavigateToURL(
+        browser(), embedded_test_server()->GetURL(kSingleFrameTestURL));
   }
 
   bool InitialSetup() {
@@ -126,13 +142,17 @@
     return true;
   }
 
-  void TearDownOnMainThread() override { delete observer_; }
+  void TearDownOnMainThread() override {
+    // Cancel unfinished download if any.
+    CancelDownloads();
+    delete observer_;
+  }
 
   // Most test cases will trigger downloads, though we don't really care if
   // download completed or not. So we cancel downloads as soon as we record
   // all the navigation events we need.
   void CancelDownloads() {
-    std::vector<content::DownloadItem*> download_items;
+    std::vector<DownloadItem*> download_items;
     content::DownloadManager* manager =
         content::BrowserContext::GetDownloadManager(browser()->profile());
     manager->GetAllDownloads(&download_items);
@@ -142,11 +162,22 @@
     }
   }
 
-  // This function needs javascript support, only works on
-  // navigation_observer_tests.html and
-  // navigation_observer_multi_frame_tests.html.
-  void ClickTestLink(const char* test_name,
-                     int number_of_navigations) {
+  DownloadItem* GetDownload() {
+    std::vector<DownloadItem*> download_items;
+    content::DownloadManager* manager =
+        content::BrowserContext::GetDownloadManager(browser()->profile());
+    manager->GetAllDownloads(&download_items);
+    EXPECT_EQ(1U, download_items.size());
+    return download_items[0];
+  }
+
+  // This function needs javascript support from the test page hosted at
+  // |page_url|. It calls "clickLink(..)" javascript function to "click" on the
+  // html element with ID specified by |element_id|, and waits for
+  // |number_of_navigations| to complete.
+  void ClickTestLink(const char* element_id,
+                     int number_of_navigations,
+                     const GURL& page_url) {
     TabStripModel* tab_strip = browser()->tab_strip_model();
     content::WebContents* current_web_contents =
         tab_strip->GetActiveWebContents();
@@ -156,13 +187,26 @@
       number_of_navigations);
     navigation_observer.StartWatchingNewWebContents();
     // Execute test.
-    std::string script = base::StringPrintf("clickLink('%s');", test_name);
+    std::string script = base::StringPrintf("clickLink('%s');", element_id);
     ASSERT_TRUE(content::ExecuteScript(current_web_contents, script));
     // Wait for navigations on current tab and new tab (if any) to finish.
     navigation_observer.Wait();
     navigation_observer.StopWatchingNewWebContents();
-    // Cancel unfinished download if any.
-    CancelDownloads();
+
+    // Since this test uses javascript to mimic clicking on a link (no actual
+    // user gesture), and DidGetUserInteraction() does not respond to
+    // ExecuteScript(), is_user_initiated field in resulting NavigationEvents
+    // will always be false. Therefore, we need to make some adjustment to
+    // relevant NavigationEvent.
+    for (auto it = navigation_map()->begin(); it != navigation_map()->end();
+         it++) {
+      for (NavigationEvent& nav_event : it->second) {
+        if (nav_event.source_url == page_url) {
+          nav_event.is_user_initiated = true;
+          return;
+        }
+      }
+    }
   }
 
   void VerifyNavigationEvent(const GURL& expected_source_url,
@@ -185,15 +229,51 @@
               actual_nav_event.has_server_redirect);
   }
 
+  void VerifyReferrerChainEntry(const GURL& expected_url,
+                                ReferrerChainEntry::URLType expected_type,
+                                const std::string& expected_ip_address,
+                                const GURL& expected_referrer_url,
+                                const GURL& expected_referrer_main_frame_url,
+                                bool expected_is_retargeting,
+                                ReferrerChainEntry actual_entry) {
+    EXPECT_EQ(expected_url.spec(), actual_entry.url());
+    EXPECT_EQ(expected_type, actual_entry.type());
+    if (expected_ip_address.empty()) {
+      ASSERT_EQ(0, actual_entry.ip_addresses_size());
+    } else {
+      ASSERT_EQ(1, actual_entry.ip_addresses_size());
+      EXPECT_EQ(expected_ip_address, actual_entry.ip_addresses(0));
+    }
+    EXPECT_EQ(expected_referrer_url.spec(), actual_entry.referrer_url());
+    EXPECT_EQ(expected_referrer_main_frame_url.spec(),
+              actual_entry.referrer_main_frame_url());
+    EXPECT_EQ(expected_is_retargeting, actual_entry.is_retargeting());
+  }
+
+  std::vector<ReferrerChainEntry> IdentifyReferrerChain(
+      DownloadItem* download) {
+    std::vector<ReferrerChainEntry> referrer_chain;
+    int download_tab_id =
+        SessionTabHelper::IdForTab(download->GetWebContents());
+    // IdentifyReferrerChain should return SUCCESS(1), SUCCESS_LANDING_PAGE(2),
+    // or SUCCESS_LANDING_REFERRER(3) in all these tests.
+    EXPECT_LE(observer_manager_->IdentifyReferrerChain(
+                  download->GetURL(), download_tab_id,
+                  2,  // kDownloadAttributionUserGestureLimit
+                  &referrer_chain),
+              SafeBrowsingNavigationObserverManager::SUCCESS_LANDING_REFERRER);
+    return referrer_chain;
+  }
+
   void VerifyHostToIpMap() {
     // Since all testing pages have the same host, there is only one entry in
     // host_to_ip_map_.
     SafeBrowsingNavigationObserverManager::HostToIpMap* actual_host_ip_map =
         host_to_ip_map();
-    ASSERT_EQ(std::size_t(1), actual_host_ip_map->size());
+    ASSERT_EQ(1U, actual_host_ip_map->size());
     auto ip_list =
         actual_host_ip_map->at(embedded_test_server()->base_url().host());
-    ASSERT_EQ(std::size_t(1), ip_list.size());
+    ASSERT_EQ(1U, ip_list.size());
     EXPECT_EQ(embedded_test_server()->host_port_pair().host(),
               ip_list.back().ip);
   }
@@ -214,63 +294,164 @@
   base::ScopedTempDir downloads_directory_;
 };
 
-// Click on a link and start download on the same page.
-IN_PROC_BROWSER_TEST_F(SBNavigationObserverBrowserTest, DirectDownload) {
-  ClickTestLink("direct_download", 1);
+// Type download URL into address bar and start download on the same page.
+IN_PROC_BROWSER_TEST_F(SBNavigationObserverBrowserTest, TypeInURLDownload) {
+  ui_test_utils::NavigateToURL(
+      browser(), embedded_test_server()->GetURL(kDownloadItemURL));
   GURL initial_url = embedded_test_server()->GetURL(kSingleFrameTestURL);
   GURL download_url = embedded_test_server()->GetURL(kDownloadItemURL);
+  std::string test_server_ip(embedded_test_server()->host_port_pair().host());
   auto nav_map = navigation_map();
   ASSERT_TRUE(nav_map);
-  ASSERT_EQ(std::size_t(1), nav_map->size());
-  ASSERT_EQ(std::size_t(1), nav_map->at(download_url).size());
-  // Since this test uses javascript to mimic clicking on a link (no actual user
-  // gesture), and DidGetUserInteraction() does not respond to ExecuteScript(),
-  // therefore is_user_initiated is false.
-  VerifyNavigationEvent(initial_url,   // source_url
-                        initial_url,   // source_main_frame_url
+  ASSERT_EQ(2U, nav_map->size());
+  ASSERT_EQ(1U, nav_map->at(download_url).size());
+  ASSERT_EQ(1U, nav_map->at(initial_url).size());
+  VerifyNavigationEvent(GURL(),       // source_url
+                        GURL(),       // source_main_frame_url
+                        initial_url,  // original_request_url
+                        initial_url,  // destination_url
+                        true,         // is_user_initiated,
+                        true,         // has_committed
+                        false,        // has_server_redirect
+                        nav_map->at(initial_url).at(0));
+  VerifyNavigationEvent(GURL(),        // source_url
+                        GURL(),        // source_main_frame_url
                         download_url,  // original_request_url
                         download_url,  // destination_url
-                        false,         // is_user_initiated,
+                        true,          // is_user_initiated,
                         false,         // has_committed
                         false,         // has_server_redirect
                         nav_map->at(download_url).at(0));
   VerifyHostToIpMap();
+
+  auto referrer_chain = IdentifyReferrerChain(GetDownload());
+  ASSERT_EQ(1U, referrer_chain.size());
+  VerifyReferrerChainEntry(download_url,                      // url
+                           ReferrerChainEntry::DOWNLOAD_URL,  // type
+                           test_server_ip,                    // ip_address
+                           GURL(),                            // referrer_url
+                           GURL(),  // referrer_main_frame_url
+                           false,   // is_retargeting
+                           referrer_chain[0]);
+}
+// Click on a link and start download on the same page.
+IN_PROC_BROWSER_TEST_F(SBNavigationObserverBrowserTest, DirectDownload) {
+  GURL initial_url = embedded_test_server()->GetURL(kSingleFrameTestURL);
+  ClickTestLink("direct_download", 1, initial_url);
+  GURL download_url = embedded_test_server()->GetURL(kDownloadItemURL);
+  std::string test_server_ip(embedded_test_server()->host_port_pair().host());
+  auto nav_map = navigation_map();
+  ASSERT_TRUE(nav_map);
+  ASSERT_EQ(2U, nav_map->size());
+  ASSERT_EQ(1U, nav_map->at(download_url).size());
+  ASSERT_EQ(1U, nav_map->at(initial_url).size());
+  VerifyNavigationEvent(GURL(),       // source_url
+                        GURL(),       // source_main_frame_url
+                        initial_url,  // original_request_url
+                        initial_url,  // destination_url
+                        true,         // is_user_initiated,
+                        true,         // has_committed
+                        false,        // has_server_redirect
+                        nav_map->at(initial_url).at(0));
+  VerifyNavigationEvent(initial_url,   // source_url
+                        initial_url,   // source_main_frame_url
+                        download_url,  // original_request_url
+                        download_url,  // destination_url
+                        true,          // is_user_initiated,
+                        false,         // has_committed
+                        false,         // has_server_redirect
+                        nav_map->at(download_url).at(0));
+  VerifyHostToIpMap();
+
+  auto referrer_chain = IdentifyReferrerChain(GetDownload());
+  ASSERT_EQ(2U, referrer_chain.size());
+  VerifyReferrerChainEntry(download_url,                      // url
+                           ReferrerChainEntry::DOWNLOAD_URL,  // type
+                           test_server_ip,                    // ip_address
+                           initial_url,                       // referrer_url
+                           initial_url,  // referrer_main_frame_url
+                           false,        // is_retargeting
+                           referrer_chain[0]);
+  VerifyReferrerChainEntry(initial_url,                       // url
+                           ReferrerChainEntry::LANDING_PAGE,  // type
+                           test_server_ip,                    // ip_address
+                           GURL(),                            // referrer_url
+                           GURL(),  // referrer_main_frame_url
+                           false,   // is_retargeting
+                           referrer_chain[1]);
 }
 
 // Click on a link with rel="noreferrer" attribute, and start download on the
 // same page.
 IN_PROC_BROWSER_TEST_F(SBNavigationObserverBrowserTest,
                        DirectDownloadNoReferrer) {
-  ClickTestLink("direct_download_noreferrer", 1);
   GURL initial_url = embedded_test_server()->GetURL(kSingleFrameTestURL);
+  ClickTestLink("direct_download_noreferrer", 1, initial_url);
   GURL download_url = embedded_test_server()->GetURL(kDownloadItemURL);
+  std::string test_server_ip(embedded_test_server()->host_port_pair().host());
   auto nav_map = navigation_map();
   ASSERT_TRUE(nav_map);
-  ASSERT_EQ(std::size_t(1), nav_map->size());
-  ASSERT_EQ(std::size_t(1), nav_map->at(download_url).size());
+  ASSERT_EQ(2U, nav_map->size());
+  ASSERT_EQ(1U, nav_map->at(download_url).size());
+  ASSERT_EQ(1U, nav_map->at(initial_url).size());
+  VerifyNavigationEvent(GURL(),       // source_url
+                        GURL(),       // source_main_frame_url
+                        initial_url,  // original_request_url
+                        initial_url,  // destination_url
+                        true,         // is_user_initiated,
+                        true,         // has_committed
+                        false,        // has_server_redirect
+                        nav_map->at(initial_url).at(0));
   VerifyNavigationEvent(initial_url,   // source_url
                         initial_url,   // source_main_frame_url
                         download_url,  // original_request_url
                         download_url,  // destination_url
-                        false,         // is_user_initiated,
+                        true,          // is_user_initiated,
                         false,         // has_committed
                         false,         // has_server_redirect
                         nav_map->at(download_url).at(0));
   VerifyHostToIpMap();
+
+  auto referrer_chain = IdentifyReferrerChain(GetDownload());
+  ASSERT_EQ(2U, referrer_chain.size());
+  VerifyReferrerChainEntry(download_url,                      // url
+                           ReferrerChainEntry::DOWNLOAD_URL,  // type
+                           test_server_ip,                    // ip_address
+                           initial_url,                       // referrer_url
+                           initial_url,  // referrer_main_frame_url
+                           false,        // is_retargeting
+                           referrer_chain[0]);
+  VerifyReferrerChainEntry(initial_url,                       // url
+                           ReferrerChainEntry::LANDING_PAGE,  // type
+                           test_server_ip,                    // ip_address
+                           GURL(),                            // referrer_url
+                           GURL(),  // referrer_main_frame_url
+                           false,   // is_retargeting
+                           referrer_chain[1]);
 }
 
 // Click on a link with rel="noreferrer" attribute, and start download in a
 // new tab using target=_blank.
 IN_PROC_BROWSER_TEST_F(SBNavigationObserverBrowserTest,
                        DirectDownloadNoReferrerTargetBlank) {
-  ClickTestLink("direct_download_noreferrer_target_blank", 1);
   GURL initial_url = embedded_test_server()->GetURL(kSingleFrameTestURL);
+  ClickTestLink("direct_download_noreferrer_target_blank", 1, initial_url);
   GURL download_url = embedded_test_server()->GetURL(kDownloadItemURL);
+  std::string test_server_ip(embedded_test_server()->host_port_pair().host());
   auto nav_map = navigation_map();
   ASSERT_TRUE(nav_map);
-  ASSERT_EQ(std::size_t(1), nav_map->size());
-  ASSERT_EQ(std::size_t(2), nav_map->at(download_url).size());
-  // The first NavigationEvent was obtained from NOIFICATION_RETARGETING.
+  ASSERT_EQ(2U, nav_map->size());
+  ASSERT_EQ(2U, nav_map->at(download_url).size());
+  ASSERT_EQ(1U, nav_map->at(initial_url).size());
+  VerifyNavigationEvent(GURL(),       // source_url
+                        GURL(),       // source_main_frame_url
+                        initial_url,  // original_request_url
+                        initial_url,  // destination_url
+                        true,         // is_user_initiated,
+                        true,         // has_committed
+                        false,        // has_server_redirect
+                        nav_map->at(initial_url).at(0));
+  // The next NavigationEvent was obtained from NOIFICATION_RETARGETING.
   // TODO(jialiul): After https://crbug.com/651895 is fixed, we'll no longer
   // listen to NOTIFICATION_RETARGETING, hence only one NavigationEvent will
   // be observed with the true initator URL. This applies to other new tab
@@ -279,11 +460,11 @@
                         initial_url,   // source_main_frame_url
                         download_url,  // original_request_url
                         download_url,  // destination_url
-                        false,         // is_user_initiated,
+                        true,          // is_user_initiated,
                         false,         // has_committed
                         false,         // has_server_redirect
                         nav_map->at(download_url).at(0));
-  // The second one is the actual navigation which triggers download.
+  // This one is the actual navigation which triggers download.
   VerifyNavigationEvent(GURL(),        // source_url
                         GURL(),        // source_main_frame_url
                         download_url,  // original_request_url
@@ -293,28 +474,55 @@
                         false,         // has_server_redirect
                         nav_map->at(download_url).at(1));
   VerifyHostToIpMap();
+
+  auto referrer_chain = IdentifyReferrerChain(GetDownload());
+  ASSERT_EQ(2U, referrer_chain.size());
+  VerifyReferrerChainEntry(download_url,                      // url
+                           ReferrerChainEntry::DOWNLOAD_URL,  // type
+                           test_server_ip,                    // ip_address
+                           initial_url,                       // referrer_url
+                           initial_url,  // referrer_main_frame_url
+                           true,         // is_retargeting
+                           referrer_chain[0]);
+  VerifyReferrerChainEntry(initial_url,                       // url
+                           ReferrerChainEntry::LANDING_PAGE,  // type
+                           test_server_ip,                    // ip_address
+                           GURL(),                            // referrer_url
+                           GURL(),  // referrer_main_frame_url
+                           false,   // is_retargeting
+                           referrer_chain[1]);
 }
 
 // Click on a link which navigates to a page then redirects to a download using
 // META HTTP-EQUIV="refresh". All transitions happen in the same tab.
 IN_PROC_BROWSER_TEST_F(SBNavigationObserverBrowserTest,
                        SingleMetaRefreshRedirect) {
-  ClickTestLink("single_meta_refresh_redirect", 2);
   GURL initial_url = embedded_test_server()->GetURL(kSingleFrameTestURL);
+  ClickTestLink("single_meta_refresh_redirect", 2, initial_url);
   GURL redirect_url = embedded_test_server()->GetURL(kRedirectURL);
   GURL download_url = embedded_test_server()->GetURL(kDownloadItemURL);
+  std::string test_server_ip(embedded_test_server()->host_port_pair().host());
   auto nav_map = navigation_map();
   ASSERT_TRUE(nav_map);
   // Since unlike server redirects client redirects commit and then generate a
   // second navigation, our observer records two NavigationEvents for this test.
-  ASSERT_EQ(std::size_t(2), nav_map->size());
-  ASSERT_EQ(std::size_t(1), nav_map->at(redirect_url).size());
-  ASSERT_EQ(std::size_t(1), nav_map->at(download_url).size());
+  ASSERT_EQ(std::size_t(3), nav_map->size());
+  ASSERT_EQ(1U, nav_map->at(redirect_url).size());
+  ASSERT_EQ(1U, nav_map->at(download_url).size());
+  ASSERT_EQ(1U, nav_map->at(initial_url).size());
+  VerifyNavigationEvent(GURL(),       // source_url
+                        GURL(),       // source_main_frame_url
+                        initial_url,  // original_request_url
+                        initial_url,  // destination_url
+                        true,         // is_user_initiated,
+                        true,         // has_committed
+                        false,        // has_server_redirect
+                        nav_map->at(initial_url).at(0));
   VerifyNavigationEvent(initial_url,   // source_url
                         initial_url,   // source_main_frame_url
                         redirect_url,  // original_request_url
                         redirect_url,  // destination_url
-                        false,         // is_user_initiated,
+                        true,          // is_user_initiated,
                         true,          // has_committed
                         false,         // has_server_redirect
                         nav_map->at(redirect_url).at(0));
@@ -327,6 +535,30 @@
                         false,         // has_server_redirect
                         nav_map->at(download_url).at(0));
   VerifyHostToIpMap();
+
+  auto referrer_chain = IdentifyReferrerChain(GetDownload());
+  ASSERT_EQ(std::size_t(3), referrer_chain.size());
+  VerifyReferrerChainEntry(download_url,                      // url
+                           ReferrerChainEntry::DOWNLOAD_URL,  // type
+                           test_server_ip,                    // ip_address
+                           redirect_url,                      // referrer_url
+                           redirect_url,  // referrer_main_frame_url
+                           false,         // is_retargeting
+                           referrer_chain[0]);
+  VerifyReferrerChainEntry(redirect_url,                         // url
+                           ReferrerChainEntry::CLIENT_REDIRECT,  // type
+                           test_server_ip,                       // ip_address
+                           initial_url,                          // referrer_url
+                           initial_url,  // referrer_main_frame_url
+                           false,        // is_retargeting
+                           referrer_chain[1]);
+  VerifyReferrerChainEntry(initial_url,                       // url
+                           ReferrerChainEntry::LANDING_PAGE,  // type
+                           test_server_ip,                    // ip_address
+                           GURL(),                            // referrer_url
+                           GURL(),  // referrer_main_frame_url
+                           false,   // is_retargeting
+                           referrer_chain[2]);
 }
 
 // https://crbug.com/667784: The test is flaky on Linux.
@@ -339,15 +571,25 @@
 // META HTTP-EQUIV="refresh". First navigation happens in target blank.
 IN_PROC_BROWSER_TEST_F(SBNavigationObserverBrowserTest,
                        MAYBE_SingleMetaRefreshRedirectTargetBlank) {
-  ClickTestLink("single_meta_refresh_redirect_target_blank", 2);
   GURL initial_url = embedded_test_server()->GetURL(kSingleFrameTestURL);
+  ClickTestLink("single_meta_refresh_redirect_target_blank", 2, initial_url);
   GURL redirect_url = embedded_test_server()->GetURL(kRedirectURL);
   GURL download_url = embedded_test_server()->GetURL(kDownloadItemURL);
+  std::string test_server_ip(embedded_test_server()->host_port_pair().host());
   auto nav_map = navigation_map();
   ASSERT_TRUE(nav_map);
-  ASSERT_EQ(std::size_t(2), nav_map->size());
-  ASSERT_EQ(std::size_t(2), nav_map->at(redirect_url).size());
-  ASSERT_EQ(std::size_t(1), nav_map->at(download_url).size());
+  ASSERT_EQ(std::size_t(3), nav_map->size());
+  ASSERT_EQ(2U, nav_map->at(redirect_url).size());
+  ASSERT_EQ(1U, nav_map->at(download_url).size());
+  ASSERT_EQ(1U, nav_map->at(initial_url).size());
+  VerifyNavigationEvent(GURL(),       // source_url
+                        GURL(),       // source_main_frame_url
+                        initial_url,  // original_request_url
+                        initial_url,  // destination_url
+                        true,         // is_user_initiated,
+                        true,         // has_committed
+                        false,        // has_server_redirect
+                        nav_map->at(initial_url).at(0));
   // TODO(jialiul): After https://crbug.com/651895 is fixed, we'll no longer
   // listen to NOTIFICATION_RETARGETING, hence only two NavigationEvents will
   // be observed with the true initator URL.
@@ -355,7 +597,7 @@
                         initial_url,   // source_main_frame_url
                         redirect_url,  // original_request_url
                         redirect_url,  // destination_url
-                        false,         // is_user_initiated,
+                        true,          // is_user_initiated,
                         false,         // has_committed
                         false,         // has_server_redirect
                         nav_map->at(redirect_url).at(0));
@@ -376,30 +618,64 @@
                         false,         // has_server_redirect
                         nav_map->at(download_url).at(0));
   VerifyHostToIpMap();
+
+  auto referrer_chain = IdentifyReferrerChain(GetDownload());
+  ASSERT_EQ(std::size_t(3), referrer_chain.size());
+  VerifyReferrerChainEntry(download_url,                      // url
+                           ReferrerChainEntry::DOWNLOAD_URL,  // type
+                           test_server_ip,                    // ip_address
+                           redirect_url,                      // referrer_url
+                           redirect_url,  // referrer_main_frame_url
+                           false,         // is_retargeting
+                           referrer_chain[0]);
+  VerifyReferrerChainEntry(redirect_url,                         // url
+                           ReferrerChainEntry::CLIENT_REDIRECT,  // type
+                           test_server_ip,                       // ip_address
+                           initial_url,                          // referrer_url
+                           initial_url,  // referrer_main_frame_url
+                           true,         // is_retargeting
+                           referrer_chain[1]);
+  VerifyReferrerChainEntry(initial_url,                       // url
+                           ReferrerChainEntry::LANDING_PAGE,  // type
+                           test_server_ip,                    // ip_address
+                           GURL(),                            // referrer_url
+                           GURL(),  // referrer_main_frame_url
+                           false,   // is_retargeting
+                           referrer_chain[2]);
 }
 
 // Click on a link which redirects twice before reaching download using
 // META HTTP-EQUIV="refresh". All transitions happen in the same tab.
 IN_PROC_BROWSER_TEST_F(SBNavigationObserverBrowserTest,
                        MultiMetaRefreshRedirects) {
-  ClickTestLink("multiple_meta_refresh_redirects", 3);
   GURL initial_url = embedded_test_server()->GetURL(kSingleFrameTestURL);
+  ClickTestLink("multiple_meta_refresh_redirects", 3, initial_url);
   GURL first_redirect_url = embedded_test_server()->GetURL(
       "/safe_browsing/download_protection/navigation_observer/"
       "double_redirect.html");
   GURL second_redirect_url = embedded_test_server()->GetURL(kRedirectURL);
   GURL download_url = embedded_test_server()->GetURL(kDownloadItemURL);
+  std::string test_server_ip(embedded_test_server()->host_port_pair().host());
   auto nav_map = navigation_map();
   ASSERT_TRUE(nav_map);
-  ASSERT_EQ(std::size_t(3), nav_map->size());
-  ASSERT_EQ(std::size_t(1), nav_map->at(first_redirect_url).size());
-  ASSERT_EQ(std::size_t(1), nav_map->at(second_redirect_url).size());
-  ASSERT_EQ(std::size_t(1), nav_map->at(download_url).size());
+  ASSERT_EQ(std::size_t(4), nav_map->size());
+  ASSERT_EQ(1U, nav_map->at(first_redirect_url).size());
+  ASSERT_EQ(1U, nav_map->at(second_redirect_url).size());
+  ASSERT_EQ(1U, nav_map->at(download_url).size());
+  ASSERT_EQ(1U, nav_map->at(initial_url).size());
+  VerifyNavigationEvent(GURL(),       // source_url
+                        GURL(),       // source_main_frame_url
+                        initial_url,  // original_request_url
+                        initial_url,  // destination_url
+                        true,         // is_user_initiated,
+                        true,         // has_committed
+                        false,        // has_server_redirect
+                        nav_map->at(initial_url).at(0));
   VerifyNavigationEvent(initial_url,         // source_url
                         initial_url,         // source_main_frame_url
                         first_redirect_url,  // original_request_url
                         first_redirect_url,  // destination_url
-                        false,               // is_user_initiated,
+                        true,                // is_user_initiated,
                         true,                // has_committed
                         false,               // has_server_redirect
                         nav_map->at(first_redirect_url).at(0));
@@ -420,47 +696,115 @@
                         false,                // has_server_redirect
                         nav_map->at(download_url).at(0));
   VerifyHostToIpMap();
+
+  auto referrer_chain = IdentifyReferrerChain(GetDownload());
+  ASSERT_EQ(std::size_t(4), referrer_chain.size());
+  VerifyReferrerChainEntry(download_url,                      // url
+                           ReferrerChainEntry::DOWNLOAD_URL,  // type
+                           test_server_ip,                    // ip_address
+                           second_redirect_url,               // referrer_url
+                           second_redirect_url,  // referrer_main_frame_url
+                           false,                // is_retargeting
+                           referrer_chain[0]);
+  VerifyReferrerChainEntry(second_redirect_url,                  // url
+                           ReferrerChainEntry::CLIENT_REDIRECT,  // type
+                           test_server_ip,                       // ip_address
+                           first_redirect_url,                   // referrer_url
+                           first_redirect_url,  // referrer_main_frame_url
+                           false,               // is_retargeting
+                           referrer_chain[1]);
+  VerifyReferrerChainEntry(first_redirect_url,                   // url
+                           ReferrerChainEntry::CLIENT_REDIRECT,  // type
+                           test_server_ip,                       // ip_address
+                           initial_url,                          // referrer_url
+                           initial_url,  // referrer_main_frame_url
+                           false,        // is_retargeting
+                           referrer_chain[2]);
+  VerifyReferrerChainEntry(initial_url,                       // url
+                           ReferrerChainEntry::LANDING_PAGE,  // type
+                           test_server_ip,                    // ip_address
+                           GURL(),                            // referrer_url
+                           GURL(),  // referrer_main_frame_url
+                           false,   // is_retargeting
+                           referrer_chain[3]);
 }
 
 // Click on a link which redirects to download using window.location.
 // All transitions happen in the same tab.
 IN_PROC_BROWSER_TEST_F(SBNavigationObserverBrowserTest,
                        WindowLocationRedirect) {
-  ClickTestLink("window_location_redirection", 1);
   GURL initial_url = embedded_test_server()->GetURL(kSingleFrameTestURL);
+  ClickTestLink("window_location_redirection", 1, initial_url);
   GURL download_url = embedded_test_server()->GetURL(kDownloadItemURL);
+  std::string test_server_ip(embedded_test_server()->host_port_pair().host());
   auto nav_map = navigation_map();
   ASSERT_TRUE(nav_map);
-  ASSERT_EQ(std::size_t(1), nav_map->size());
-  ASSERT_EQ(std::size_t(1), nav_map->at(download_url).size());
+  ASSERT_EQ(2U, nav_map->size());
+  ASSERT_EQ(1U, nav_map->at(download_url).size());
+  ASSERT_EQ(1U, nav_map->at(initial_url).size());
+  VerifyNavigationEvent(GURL(),       // source_url
+                        GURL(),       // source_main_frame_url
+                        initial_url,  // original_request_url
+                        initial_url,  // destination_url
+                        true,         // is_user_initiated,
+                        true,         // has_committed
+                        false,        // has_server_redirect
+                        nav_map->at(initial_url).at(0));
   VerifyNavigationEvent(initial_url,   // source_url
                         initial_url,   // source_main_frame_url
                         download_url,  // original_request_url
                         download_url,  // destination_url
-                        false,         // is_user_initiated,
+                        true,          // is_user_initiated,
                         false,         // has_committed
                         false,         // has_server_redirect
                         nav_map->at(download_url).at(0));
+
+  auto referrer_chain = IdentifyReferrerChain(GetDownload());
+  ASSERT_EQ(2U, referrer_chain.size());
+  VerifyReferrerChainEntry(download_url,                      // url
+                           ReferrerChainEntry::DOWNLOAD_URL,  // type
+                           test_server_ip,                    // ip_address
+                           initial_url,                       // referrer_url
+                           initial_url,  // referrer_main_frame_url
+                           false,        // is_retargeting
+                           referrer_chain[0]);
+  VerifyReferrerChainEntry(initial_url,                       // url
+                           ReferrerChainEntry::LANDING_PAGE,  // type
+                           test_server_ip,                    // ip_address
+                           GURL(),                            // referrer_url
+                           GURL(),  // referrer_main_frame_url
+                           false,   // is_retargeting
+                           referrer_chain[1]);
 }
 
 // Click on a link which redirects twice until it reaches download using a
 // mixture of meta refresh and window.location. All transitions happen in the
 // same tab.
 IN_PROC_BROWSER_TEST_F(SBNavigationObserverBrowserTest, MixRedirects) {
-  ClickTestLink("mix_redirects", 2);
-  GURL redirect_url = embedded_test_server()->GetURL(kRedirectURL);
   GURL initial_url = embedded_test_server()->GetURL(kSingleFrameTestURL);
+  ClickTestLink("mix_redirects", 2, initial_url);
+  GURL redirect_url = embedded_test_server()->GetURL(kRedirectURL);
   GURL download_url = embedded_test_server()->GetURL(kDownloadItemURL);
+  std::string test_server_ip(embedded_test_server()->host_port_pair().host());
   auto nav_map = navigation_map();
   ASSERT_TRUE(nav_map);
-  ASSERT_EQ(std::size_t(2), nav_map->size());
-  ASSERT_EQ(std::size_t(1), nav_map->at(redirect_url).size());
-  ASSERT_EQ(std::size_t(1), nav_map->at(download_url).size());
+  ASSERT_EQ(std::size_t(3), nav_map->size());
+  ASSERT_EQ(1U, nav_map->at(redirect_url).size());
+  ASSERT_EQ(1U, nav_map->at(download_url).size());
+  ASSERT_EQ(1U, nav_map->at(initial_url).size());
+  VerifyNavigationEvent(GURL(),       // source_url
+                        GURL(),       // source_main_frame_url
+                        initial_url,  // original_request_url
+                        initial_url,  // destination_url
+                        true,         // is_user_initiated,
+                        true,         // has_committed
+                        false,        // has_server_redirect
+                        nav_map->at(initial_url).at(0));
   VerifyNavigationEvent(initial_url,   // source_url
                         initial_url,   // source_main_frame_url
                         redirect_url,  // original_request_url
                         redirect_url,  // destination_url
-                        false,         // is_user_initiated,
+                        true,          // is_user_initiated,
                         true,          // has_committed
                         false,         // has_server_redirect
                         nav_map->at(redirect_url).at(0));
@@ -473,24 +817,58 @@
                         false,         // has_server_redirect
                         nav_map->at(download_url).at(0));
   VerifyHostToIpMap();
+
+  auto referrer_chain = IdentifyReferrerChain(GetDownload());
+  ASSERT_EQ(std::size_t(3), referrer_chain.size());
+  VerifyReferrerChainEntry(download_url,                      // url
+                           ReferrerChainEntry::DOWNLOAD_URL,  // type
+                           test_server_ip,                    // ip_address
+                           redirect_url,                      // referrer_url
+                           redirect_url,  // referrer_main_frame_url
+                           false,         // is_retargeting
+                           referrer_chain[0]);
+  VerifyReferrerChainEntry(redirect_url,                         // url
+                           ReferrerChainEntry::CLIENT_REDIRECT,  // type
+                           test_server_ip,                       // ip_address
+                           initial_url,                          // referrer_url
+                           initial_url,  // referrer_main_frame_url
+                           false,        // is_retargeting
+                           referrer_chain[1]);
+  VerifyReferrerChainEntry(initial_url,                       // url
+                           ReferrerChainEntry::LANDING_PAGE,  // type
+                           test_server_ip,                    // ip_address
+                           GURL(),                            // referrer_url
+                           GURL(),  // referrer_main_frame_url
+                           false,   // is_retargeting
+                           referrer_chain[2]);
 }
 
 // Use javascript to open download in a new tab.
 IN_PROC_BROWSER_TEST_F(SBNavigationObserverBrowserTest, NewTabDownload) {
-  ClickTestLink("new_tab_download", 2);
   GURL initial_url = embedded_test_server()->GetURL(kSingleFrameTestURL);
+  ClickTestLink("new_tab_download", 2, initial_url);
   GURL download_url = embedded_test_server()->GetURL(kDownloadItemURL);
   GURL blank_url = GURL(url::kAboutBlankURL);
+  std::string test_server_ip(embedded_test_server()->host_port_pair().host());
   auto nav_map = navigation_map();
   ASSERT_TRUE(nav_map);
-  ASSERT_EQ(std::size_t(2), nav_map->size());
-  ASSERT_EQ(std::size_t(2), nav_map->at(blank_url).size());
-  ASSERT_EQ(std::size_t(1), nav_map->at(download_url).size());
+  ASSERT_EQ(std::size_t(3), nav_map->size());
+  ASSERT_EQ(2U, nav_map->at(blank_url).size());
+  ASSERT_EQ(1U, nav_map->at(download_url).size());
+  ASSERT_EQ(1U, nav_map->at(initial_url).size());
+  VerifyNavigationEvent(GURL(),       // source_url
+                        GURL(),       // source_main_frame_url
+                        initial_url,  // original_request_url
+                        initial_url,  // destination_url
+                        true,         // is_user_initiated,
+                        true,         // has_committed
+                        false,        // has_server_redirect
+                        nav_map->at(initial_url).at(0));
   VerifyNavigationEvent(initial_url,  // source_url
                         initial_url,  // source_main_frame_url
                         blank_url,    // original_request_url
                         blank_url,    // destination_url
-                        false,        // is_user_initiated,
+                        true,         // is_user_initiated,
                         false,        // has_committed
                         false,        // has_server_redirect
                         nav_map->at(blank_url).at(0));
@@ -518,26 +896,60 @@
   EXPECT_EQ(nav_map->at(download_url).at(0).source_tab_id,
             nav_map->at(download_url).at(0).target_tab_id);
   VerifyHostToIpMap();
+
+  auto referrer_chain = IdentifyReferrerChain(GetDownload());
+  ASSERT_EQ(std::size_t(3), referrer_chain.size());
+  VerifyReferrerChainEntry(download_url,                      // url
+                           ReferrerChainEntry::DOWNLOAD_URL,  // type
+                           test_server_ip,                    // ip_address
+                           blank_url,                         // referrer_url
+                           blank_url,  // referrer_main_frame_url
+                           false,      // is_retargeting
+                           referrer_chain[0]);
+  VerifyReferrerChainEntry(blank_url,                            // url
+                           ReferrerChainEntry::CLIENT_REDIRECT,  // type
+                           "",                                   // ip_address
+                           initial_url,                          // referrer_url
+                           initial_url,  // referrer_main_frame_url
+                           true,         // is_retargeting
+                           referrer_chain[1]);
+  VerifyReferrerChainEntry(initial_url,                       // url
+                           ReferrerChainEntry::LANDING_PAGE,  // type
+                           test_server_ip,                    // ip_address
+                           GURL(),                            // referrer_url
+                           GURL(),  // referrer_main_frame_url
+                           false,   // is_retargeting
+                           referrer_chain[2]);
 }
 
 // Use javascript to open download in a new tab and download has a data url.
 IN_PROC_BROWSER_TEST_F(SBNavigationObserverBrowserTest,
                        NewTabDownloadWithDataURL) {
-  ClickTestLink("new_tab_download_with_data_url", 2);
   GURL initial_url = embedded_test_server()->GetURL(kSingleFrameTestURL);
+  ClickTestLink("new_tab_download_with_data_url", 2, initial_url);
   GURL download_url = GURL(kDownloadDataURL);
-  GURL blank_url = GURL("about:blank");
+  GURL blank_url = GURL(url::kAboutBlankURL);
+  std::string test_server_ip(embedded_test_server()->host_port_pair().host());
   auto nav_map = navigation_map();
   ASSERT_TRUE(nav_map);
-  ASSERT_EQ(std::size_t(2), nav_map->size());
-  ASSERT_EQ(std::size_t(2), nav_map->at(blank_url).size());
-  ASSERT_EQ(std::size_t(1), nav_map->at(download_url).size());
+  ASSERT_EQ(std::size_t(3), nav_map->size());
+  ASSERT_EQ(2U, nav_map->at(blank_url).size());
+  ASSERT_EQ(1U, nav_map->at(download_url).size());
+  ASSERT_EQ(1U, nav_map->at(initial_url).size());
+  VerifyNavigationEvent(GURL(),       // source_url
+                        GURL(),       // source_main_frame_url
+                        initial_url,  // original_request_url
+                        initial_url,  // destination_url
+                        true,         // is_user_initiated,
+                        true,         // has_committed
+                        false,        // has_server_redirect
+                        nav_map->at(initial_url).at(0));
   // The first one comes from NOTIFICATION_RETARGETING.
   VerifyNavigationEvent(initial_url,  // source_url
                         initial_url,  // source_main_frame_url
                         blank_url,    // original_request_url
                         blank_url,    // destination_url
-                        false,        // is_user_initiated,
+                        true,         // is_user_initiated,
                         false,        // has_committed
                         false,        // has_server_redirect
                         nav_map->at(blank_url).at(0));
@@ -564,8 +976,31 @@
                         nav_map->at(download_url).at(0));
   EXPECT_TRUE(nav_map->at(download_url).at(0).source_tab_id ==
               nav_map->at(download_url).at(0).target_tab_id);
-  // Since data url does does not have IP, host_to_ip_map_ should be empty.
-  EXPECT_EQ(std::size_t(0), host_to_ip_map()->size());
+  VerifyHostToIpMap();
+
+  auto referrer_chain = IdentifyReferrerChain(GetDownload());
+  ASSERT_EQ(std::size_t(3), referrer_chain.size());
+  VerifyReferrerChainEntry(download_url,                      // url
+                           ReferrerChainEntry::DOWNLOAD_URL,  // type
+                           "",                                // ip_address
+                           blank_url,                         // referrer_url
+                           blank_url,  // referrer_main_frame_url
+                           false,      // is_retargeting
+                           referrer_chain[0]);
+  VerifyReferrerChainEntry(blank_url,                            // url
+                           ReferrerChainEntry::CLIENT_REDIRECT,  // type
+                           "",                                   // ip_address
+                           initial_url,                          // referrer_url
+                           initial_url,  // referrer_main_frame_url
+                           true,         // is_retargeting
+                           referrer_chain[1]);
+  VerifyReferrerChainEntry(initial_url,                       // url
+                           ReferrerChainEntry::LANDING_PAGE,  // type
+                           test_server_ip,                    // ip_address
+                           GURL(),                            // referrer_url
+                           GURL(),  // referrer_main_frame_url
+                           false,   // is_retargeting
+                           referrer_chain[2]);
 }
 
 // TODO(jialiul): Need to figure out why this test is failing on Windows and
@@ -574,25 +1009,52 @@
 // Download via html5 file API.
 IN_PROC_BROWSER_TEST_F(SBNavigationObserverBrowserTest,
                        MAYBE_DownloadViaHTML5FileApi) {
-  ClickTestLink("html5_file_api", 1);
   GURL initial_url = embedded_test_server()->GetURL(kSingleFrameTestURL);
+  ClickTestLink("html5_file_api", 1, initial_url);
   std::string download_url_str =
       base::StringPrintf("filesystem:%stemporary/test.exe",
                          embedded_test_server()->base_url().spec().c_str());
   GURL download_url = GURL(download_url_str);
+  std::string test_server_ip(embedded_test_server()->host_port_pair().host());
   auto nav_map = navigation_map();
   ASSERT_TRUE(nav_map);
-  ASSERT_EQ(std::size_t(1), nav_map->size());
-  ASSERT_EQ(std::size_t(1), nav_map->at(download_url).size());
+  ASSERT_EQ(2U, nav_map->size());
+  ASSERT_EQ(1U, nav_map->at(download_url).size());
+  ASSERT_EQ(1U, nav_map->at(initial_url).size());
+  VerifyNavigationEvent(GURL(),       // source_url
+                        GURL(),       // source_main_frame_url
+                        initial_url,  // original_request_url
+                        initial_url,  // destination_url
+                        true,         // is_user_initiated,
+                        true,         // has_committed
+                        false,        // has_server_redirect
+                        nav_map->at(initial_url).at(0));
   VerifyNavigationEvent(initial_url,   // source_url
                         initial_url,   // source_main_frame_url
                         download_url,  // original_request_url
                         download_url,  // destination_url
-                        false,         // is_user_initiated,
+                        true,          // is_user_initiated,
                         false,         // has_committed
                         false,         // has_server_redirect
                         nav_map->at(download_url).at(0));
   VerifyHostToIpMap();
+
+  auto referrer_chain = IdentifyReferrerChain(GetDownload());
+  ASSERT_EQ(2U, referrer_chain.size());
+  VerifyReferrerChainEntry(download_url,                      // url
+                           ReferrerChainEntry::DOWNLOAD_URL,  // type
+                           "",                                // ip_address
+                           initial_url,                       // referrer_url
+                           initial_url,  // referrer_main_frame_url
+                           false,        // is_retargeting
+                           referrer_chain[0]);
+  VerifyReferrerChainEntry(initial_url,                       // url
+                           ReferrerChainEntry::LANDING_PAGE,  // type
+                           test_server_ip,                    // ip_address
+                           GURL(),                            // referrer_url
+                           GURL(),  // referrer_main_frame_url
+                           false,   // is_retargeting
+                           referrer_chain[1]);
 }
 
 // Click a link in a subframe and start download.
@@ -602,23 +1064,33 @@
       browser(), embedded_test_server()->GetURL(kMultiFrameTestURL));
   std::string test_name =
       base::StringPrintf("%s', '%s", "iframe1", "iframe_direct_download");
-  ClickTestLink(test_name.c_str(), 1);
   GURL initial_url = embedded_test_server()->GetURL(kSingleFrameTestURL);
   GURL multi_frame_test_url =
       embedded_test_server()->GetURL(kMultiFrameTestURL);
   GURL iframe_url = embedded_test_server()->GetURL(kIframeDirectDownloadURL);
+  ClickTestLink(test_name.c_str(), 1, iframe_url);
   GURL iframe_retargeting_url =
       embedded_test_server()->GetURL(kIframeRetargetingURL);
   GURL download_url = embedded_test_server()->GetURL(kDownloadItemURL);
+  std::string test_server_ip(embedded_test_server()->host_port_pair().host());
   auto nav_map = navigation_map();
   ASSERT_TRUE(nav_map);
-  ASSERT_EQ(std::size_t(4), nav_map->size());
-  ASSERT_EQ(std::size_t(1), nav_map->at(multi_frame_test_url).size());
-  ASSERT_EQ(std::size_t(1), nav_map->at(iframe_url).size());
-  ASSERT_EQ(std::size_t(1), nav_map->at(iframe_retargeting_url).size());
-  ASSERT_EQ(std::size_t(1), nav_map->at(download_url).size());
-  VerifyNavigationEvent(initial_url,           // source_url
-                        initial_url,           // source_main_frame_url
+  ASSERT_EQ(std::size_t(5), nav_map->size());
+  ASSERT_EQ(1U, nav_map->at(multi_frame_test_url).size());
+  ASSERT_EQ(1U, nav_map->at(iframe_url).size());
+  ASSERT_EQ(1U, nav_map->at(iframe_retargeting_url).size());
+  ASSERT_EQ(1U, nav_map->at(download_url).size());
+  ASSERT_EQ(1U, nav_map->at(initial_url).size());
+  VerifyNavigationEvent(GURL(),       // source_url
+                        GURL(),       // source_main_frame_url
+                        initial_url,  // original_request_url
+                        initial_url,  // destination_url
+                        true,         // is_user_initiated,
+                        true,         // has_committed
+                        false,        // has_server_redirect
+                        nav_map->at(initial_url).at(0));
+  VerifyNavigationEvent(GURL(),                // source_url
+                        GURL(),                // source_main_frame_url
                         multi_frame_test_url,  // original_request_url
                         multi_frame_test_url,  // destination_url
                         true,                  // is_user_initiated,
@@ -645,11 +1117,28 @@
                         multi_frame_test_url,  // source_main_frame_url
                         download_url,          // original_request_url
                         download_url,          // destination_url
-                        false,                 // is_user_initiated,
+                        true,                  // is_user_initiated,
                         false,                 // has_committed
                         false,                 // has_server_redirect
                         nav_map->at(download_url).at(0));
   VerifyHostToIpMap();
+
+  auto referrer_chain = IdentifyReferrerChain(GetDownload());
+  ASSERT_EQ(2U, referrer_chain.size());
+  VerifyReferrerChainEntry(download_url,                      // url
+                           ReferrerChainEntry::DOWNLOAD_URL,  // type
+                           test_server_ip,                    // ip_address
+                           iframe_url,                        // referrer_url
+                           multi_frame_test_url,  // referrer_main_frame_url
+                           false,                 // is_retargeting
+                           referrer_chain[0]);
+  VerifyReferrerChainEntry(iframe_url,                        // url
+                           ReferrerChainEntry::LANDING_PAGE,  // type
+                           test_server_ip,                    // ip_address
+                           GURL(),                            // referrer_url
+                           multi_frame_test_url,  // referrer_main_frame_url
+                           false,                 // is_retargeting
+                           referrer_chain[1]);
 }
 
 // Click a link in a subframe and open download in a new tab.
@@ -659,25 +1148,35 @@
       browser(), embedded_test_server()->GetURL(kMultiFrameTestURL));
   std::string test_name =
       base::StringPrintf("%s', '%s", "iframe2", "iframe_new_tab_download");
-  ClickTestLink(test_name.c_str(), 2);
   GURL initial_url = embedded_test_server()->GetURL(kSingleFrameTestURL);
   GURL multi_frame_test_url =
       embedded_test_server()->GetURL(kMultiFrameTestURL);
   GURL iframe_url = embedded_test_server()->GetURL(kIframeDirectDownloadURL);
   GURL iframe_retargeting_url =
       embedded_test_server()->GetURL(kIframeRetargetingURL);
+  ClickTestLink(test_name.c_str(), 2, iframe_retargeting_url);
   GURL blank_url = GURL(url::kAboutBlankURL);
   GURL download_url = embedded_test_server()->GetURL(kDownloadItemURL);
+  std::string test_server_ip(embedded_test_server()->host_port_pair().host());
   auto nav_map = navigation_map();
   ASSERT_TRUE(nav_map);
-  ASSERT_EQ(std::size_t(5), nav_map->size());
-  ASSERT_EQ(std::size_t(1), nav_map->at(multi_frame_test_url).size());
-  ASSERT_EQ(std::size_t(1), nav_map->at(iframe_url).size());
-  ASSERT_EQ(std::size_t(1), nav_map->at(iframe_retargeting_url).size());
-  ASSERT_EQ(std::size_t(2), nav_map->at(blank_url).size());
-  ASSERT_EQ(std::size_t(1), nav_map->at(download_url).size());
-  VerifyNavigationEvent(initial_url,           // source_url
-                        initial_url,           // source_main_frame_url
+  ASSERT_EQ(std::size_t(6), nav_map->size());
+  ASSERT_EQ(1U, nav_map->at(multi_frame_test_url).size());
+  ASSERT_EQ(1U, nav_map->at(iframe_url).size());
+  ASSERT_EQ(1U, nav_map->at(iframe_retargeting_url).size());
+  ASSERT_EQ(2U, nav_map->at(blank_url).size());
+  ASSERT_EQ(1U, nav_map->at(download_url).size());
+  ASSERT_EQ(1U, nav_map->at(initial_url).size());
+  VerifyNavigationEvent(GURL(),       // source_url
+                        GURL(),       // source_main_frame_url
+                        initial_url,  // original_request_url
+                        initial_url,  // destination_url
+                        true,         // is_user_initiated,
+                        true,         // has_committed
+                        false,        // has_server_redirect
+                        nav_map->at(initial_url).at(0));
+  VerifyNavigationEvent(GURL(),                // source_url
+                        GURL(),                // source_main_frame_url
                         multi_frame_test_url,  // original_request_url
                         multi_frame_test_url,  // destination_url
                         true,                  // is_user_initiated,
@@ -704,7 +1203,7 @@
                         multi_frame_test_url,    // source_main_frame_url
                         blank_url,               // original_request_url
                         blank_url,               // destination_url
-                        false,                   // is_user_initiated,
+                        true,                    // is_user_initiated,
                         false,                   // has_committed
                         false,                   // has_server_redirect
                         nav_map->at(blank_url).at(0));
@@ -725,6 +1224,207 @@
                         false,         // has_server_redirect
                         nav_map->at(download_url).at(0));
   VerifyHostToIpMap();
+
+  auto referrer_chain = IdentifyReferrerChain(GetDownload());
+  EXPECT_EQ(std::size_t(3), referrer_chain.size());
+  VerifyReferrerChainEntry(download_url,                      // url
+                           ReferrerChainEntry::DOWNLOAD_URL,  // type
+                           test_server_ip,                    // ip_address
+                           blank_url,                         // referrer_url
+                           blank_url,  // referrer_main_frame_url
+                           false,      // is_retargeting
+                           referrer_chain[0]);
+  VerifyReferrerChainEntry(blank_url,                            // url
+                           ReferrerChainEntry::CLIENT_REDIRECT,  // type
+                           "",                                   // ip_address
+                           iframe_retargeting_url,               // referrer_url
+                           multi_frame_test_url,  // referrer_main_frame_url
+                           true,                  // is_retargeting
+                           referrer_chain[1]);
+  VerifyReferrerChainEntry(iframe_retargeting_url,            // url
+                           ReferrerChainEntry::LANDING_PAGE,  // type
+                           test_server_ip,                    // ip_address
+                           GURL(),                            // referrer_url
+                           multi_frame_test_url,  // referrer_main_frame_url
+                           false,                 // is_retargeting
+                           referrer_chain[2]);
+}
+
+// Click a link which redirects to the landing page, and then click on the
+// landing page to trigger download.
+IN_PROC_BROWSER_TEST_F(SBNavigationObserverBrowserTest, CompleteReferrerChain) {
+  GURL initial_url = embedded_test_server()->GetURL(kSingleFrameTestURL);
+  ClickTestLink("complete_referrer_chain", 2, initial_url);
+  GURL redirect_url = embedded_test_server()->GetURL(kRedirectToLandingURL);
+  GURL landing_url = embedded_test_server()->GetURL(kLandingURL);
+  ClickTestLink("download_on_landing_page", 1, landing_url);
+  GURL download_url = embedded_test_server()->GetURL(kDownloadItemURL);
+  std::string test_server_ip(embedded_test_server()->host_port_pair().host());
+  auto nav_map = navigation_map();
+  ASSERT_TRUE(nav_map);
+  ASSERT_EQ(std::size_t(4), nav_map->size());
+  ASSERT_EQ(1U, nav_map->at(redirect_url).size());
+  ASSERT_EQ(1U, nav_map->at(landing_url).size());
+  ASSERT_EQ(1U, nav_map->at(download_url).size());
+  ASSERT_EQ(1U, nav_map->at(initial_url).size());
+  VerifyNavigationEvent(GURL(),       // source_url
+                        GURL(),       // source_main_frame_url
+                        initial_url,  // original_request_url
+                        initial_url,  // destination_url
+                        true,         // is_user_initiated,
+                        true,         // has_committed
+                        false,        // has_server_redirect
+                        nav_map->at(initial_url).at(0));
+  VerifyNavigationEvent(initial_url,   // source_url
+                        initial_url,   // source_main_frame_url
+                        redirect_url,  // original_request_url
+                        redirect_url,  // destination_url
+                        true,          // is_user_initiated,
+                        true,          // has_committed
+                        false,         // has_server_redirect
+                        nav_map->at(redirect_url).at(0));
+  VerifyNavigationEvent(redirect_url,  // source_url
+                        redirect_url,  // source_main_frame_url
+                        landing_url,   // original_request_url
+                        landing_url,   // destination_url
+                        false,         // is_user_initiated,
+                        true,          // has_committed
+                        false,         // has_server_redirect
+                        nav_map->at(landing_url).at(0));
+  VerifyNavigationEvent(landing_url,   // source_url
+                        landing_url,   // source_main_frame_url
+                        download_url,  // original_request_url
+                        download_url,  // destination_url
+                        true,          // is_user_initiated,
+                        false,         // has_committed
+                        false,         // has_server_redirect
+                        nav_map->at(download_url).at(0));
+  VerifyHostToIpMap();
+
+  auto referrer_chain = IdentifyReferrerChain(GetDownload());
+  EXPECT_EQ(std::size_t(4), referrer_chain.size());
+  VerifyReferrerChainEntry(download_url,                      // url
+                           ReferrerChainEntry::DOWNLOAD_URL,  // type
+                           test_server_ip,                    // ip_address
+                           landing_url,                       // referrer_url
+                           landing_url,  // referrer_main_frame_url
+                           false,        // is_retargeting
+                           referrer_chain[0]);
+  VerifyReferrerChainEntry(landing_url,                       // url
+                           ReferrerChainEntry::LANDING_PAGE,  // type
+                           test_server_ip,                    // ip_address
+                           redirect_url,                      // referrer_url
+                           redirect_url,  // referrer_main_frame_url
+                           false,         // is_retargeting
+                           referrer_chain[1]);
+  VerifyReferrerChainEntry(redirect_url,                         // url
+                           ReferrerChainEntry::CLIENT_REDIRECT,  // type
+                           test_server_ip,                       // ip_address
+                           initial_url,                          // referrer_url
+                           initial_url,  // referrer_main_frame_url
+                           false,        // is_retargeting
+                           referrer_chain[2]);
+  VerifyReferrerChainEntry(
+      initial_url,                           // url
+      ReferrerChainEntry::LANDING_REFERRER,  // type
+      test_server_ip,                        // ip_address
+      GURL(),  // referrer_url is empty since this beyonds 2 clicks.
+      GURL(),  // referrer_main_frame_url is empty for the same reason.
+      false,   // is_retargeting
+      referrer_chain[3]);
+}
+
+// Click three links before reaching download.
+IN_PROC_BROWSER_TEST_F(SBNavigationObserverBrowserTest,
+                       ReferrerAttributionWithinTwoUserGestures) {
+  GURL initial_url = embedded_test_server()->GetURL(kSingleFrameTestURL);
+  ClickTestLink("attribution_within_two_user_gestures", 1, initial_url);
+  GURL page_before_landing_referrer_url =
+      embedded_test_server()->GetURL(kPageBeforeLandingReferrerURL);
+  ClickTestLink("link_to_landing_referrer", 1,
+                page_before_landing_referrer_url);
+  GURL landing_referrer_url =
+      embedded_test_server()->GetURL(kLandingReferrerURL);
+  ClickTestLink("link_to_landing", 1, landing_referrer_url);
+  GURL landing_url = embedded_test_server()->GetURL(kLandingURL);
+  ClickTestLink("download_on_landing_page", 1, landing_url);
+  GURL download_url = embedded_test_server()->GetURL(kDownloadItemURL);
+  std::string test_server_ip(embedded_test_server()->host_port_pair().host());
+  auto nav_map = navigation_map();
+  ASSERT_TRUE(nav_map);
+  ASSERT_EQ(std::size_t(5), nav_map->size());
+  ASSERT_EQ(1U, nav_map->at(initial_url).size());
+  ASSERT_EQ(1U, nav_map->at(page_before_landing_referrer_url).size());
+  ASSERT_EQ(1U, nav_map->at(landing_referrer_url).size());
+  ASSERT_EQ(1U, nav_map->at(landing_url).size());
+  ASSERT_EQ(1U, nav_map->at(download_url).size());
+  VerifyNavigationEvent(GURL(),       // source_url
+                        GURL(),       // source_main_frame_url
+                        initial_url,  // original_request_url
+                        initial_url,  // destination_url
+                        true,         // is_user_initiated,
+                        true,         // has_committed
+                        false,        // has_server_redirect
+                        nav_map->at(initial_url).at(0));
+  VerifyNavigationEvent(initial_url,  // source_url
+                        initial_url,  // source_main_frame_url
+                        page_before_landing_referrer_url,  // original_request
+                        page_before_landing_referrer_url,  // destination_url
+                        true,                              // is_user_initiated,
+                        true,                              // has_committed
+                        false,  // has_server_redirect
+                        nav_map->at(page_before_landing_referrer_url).at(0));
+  VerifyNavigationEvent(page_before_landing_referrer_url,  // source_url
+                        page_before_landing_referrer_url,  // source_main_frame
+                        landing_referrer_url,  // original_request_url
+                        landing_referrer_url,  // destination_url
+                        true,                  // is_user_initiated,
+                        true,                  // has_committed
+                        false,                 // has_server_redirect
+                        nav_map->at(landing_referrer_url).at(0));
+  VerifyNavigationEvent(landing_referrer_url,  // source_url
+                        landing_referrer_url,  // source_main_frame
+                        landing_url,           // original_request_url
+                        landing_url,           // destination_url
+                        true,                  // is_user_initiated,
+                        true,                  // has_committed
+                        false,                 // has_server_redirect
+                        nav_map->at(landing_url).at(0));
+  VerifyNavigationEvent(landing_url,   // source_url
+                        landing_url,   // source_main_frame_url
+                        download_url,  // original_request_url
+                        download_url,  // destination_url
+                        true,          // is_user_initiated,
+                        false,         // has_committed
+                        false,         // has_server_redirect
+                        nav_map->at(download_url).at(0));
+  VerifyHostToIpMap();
+
+  auto referrer_chain = IdentifyReferrerChain(GetDownload());
+  EXPECT_EQ(std::size_t(3), referrer_chain.size());
+  VerifyReferrerChainEntry(download_url,                      // url
+                           ReferrerChainEntry::DOWNLOAD_URL,  // type
+                           test_server_ip,                    // ip_address
+                           landing_url,                       // referrer_url
+                           landing_url,  // referrer_main_frame_url
+                           false,        // is_retargeting
+                           referrer_chain[0]);
+  VerifyReferrerChainEntry(landing_url,                       // url
+                           ReferrerChainEntry::LANDING_PAGE,  // type
+                           test_server_ip,                    // ip_address
+                           landing_referrer_url,              // referrer_url
+                           landing_referrer_url,  // referrer_main_frame_url
+                           false,                 // is_retargeting
+                           referrer_chain[1]);
+  VerifyReferrerChainEntry(
+      landing_referrer_url,                  // url
+      ReferrerChainEntry::LANDING_REFERRER,  // type
+      test_server_ip,                        // ip_address
+      GURL(),  // referrer_url is empty since this beyonds 2 clicks.
+      GURL(),  // referrer_main_frame_url is empty for the same reason.
+      false,   // is_retargeting
+      referrer_chain[2]);
+  // page_before_landing_referrer_url is not in referrer chain.
 }
 
 // Server-side redirect.
@@ -734,37 +1434,60 @@
   GURL request_url =
       embedded_test_server()->GetURL("/server-redirect?" + download_url.spec());
   ui_test_utils::NavigateToURL(browser(), request_url);
-  CancelDownloads();
+  std::string test_server_ip(embedded_test_server()->host_port_pair().host());
   auto nav_map = navigation_map();
   ASSERT_TRUE(nav_map);
-  ASSERT_EQ(std::size_t(1), nav_map->size());
-  ASSERT_EQ(std::size_t(1), nav_map->at(download_url).size());
-  VerifyNavigationEvent(initial_url,   // source_url
-                        initial_url,   // source_main_frame_url
+  ASSERT_EQ(2U, nav_map->size());
+  ASSERT_EQ(1U, nav_map->at(download_url).size());
+  ASSERT_EQ(1U, nav_map->at(initial_url).size());
+  VerifyNavigationEvent(GURL(),       // source_url
+                        GURL(),       // source_main_frame_url
+                        initial_url,  // original_request_url
+                        initial_url,  // destination_url
+                        true,         // is_user_initiated,
+                        true,         // has_committed
+                        false,        // has_server_redirect
+                        nav_map->at(initial_url).at(0));
+  VerifyNavigationEvent(GURL(),        // source_url
+                        GURL(),        // source_main_frame_url
                         request_url,   // original_request_url
                         download_url,  // destination_url
                         true,          // is_user_initiated,
                         false,         // has_committed
                         true,          // has_server_redirect
                         nav_map->at(download_url).at(0));
+
+  auto referrer_chain = IdentifyReferrerChain(GetDownload());
+  ASSERT_EQ(1U, referrer_chain.size());
+  VerifyReferrerChainEntry(download_url,                      // url
+                           ReferrerChainEntry::DOWNLOAD_URL,  // type
+                           test_server_ip,                    // ip_address
+                           GURL(),                            // referrer_url
+                           GURL(),  // referrer_main_frame_url
+                           false,   // is_retargeting
+                           referrer_chain[0]);
 }
 
 // host_to_ip_map_ size should increase by one after a new navigation.
 IN_PROC_BROWSER_TEST_F(SBNavigationObserverBrowserTest, AddIPMapping) {
+  GURL initial_url = embedded_test_server()->GetURL(kSingleFrameTestURL);
   auto ip_map = host_to_ip_map();
   std::string test_server_host(embedded_test_server()->base_url().host());
+  ip_map->clear();
   ip_map->insert(
       std::make_pair(test_server_host, std::vector<ResolvedIPAddress>()));
   ASSERT_EQ(std::size_t(0), ip_map->at(test_server_host).size());
-  ClickTestLink("direct_download", 1);
-  EXPECT_EQ(std::size_t(1), ip_map->at(test_server_host).size());
+  ClickTestLink("direct_download", 1, initial_url);
+  EXPECT_EQ(1U, ip_map->at(test_server_host).size());
   EXPECT_EQ(embedded_test_server()->host_port_pair().host(),
             ip_map->at(test_server_host).back().ip);
 }
 
 // If we have already seen an IP associated with a host, update its timestamp.
 IN_PROC_BROWSER_TEST_F(SBNavigationObserverBrowserTest, IPListDedup) {
+  GURL initial_url = embedded_test_server()->GetURL(kSingleFrameTestURL);
   auto ip_map = host_to_ip_map();
+  ip_map->clear();
   std::string test_server_host(embedded_test_server()->base_url().host());
   ip_map->insert(
       std::make_pair(test_server_host, std::vector<ResolvedIPAddress>()));
@@ -772,9 +1495,9 @@
   ip_map->at(test_server_host)
       .push_back(ResolvedIPAddress(
           yesterday, embedded_test_server()->host_port_pair().host()));
-  ASSERT_EQ(std::size_t(1), ip_map->at(test_server_host).size());
-  ClickTestLink("direct_download", 1);
-  EXPECT_EQ(std::size_t(1), ip_map->at(test_server_host).size());
+  ASSERT_EQ(1U, ip_map->at(test_server_host).size());
+  ClickTestLink("direct_download", 1, initial_url);
+  EXPECT_EQ(1U, ip_map->at(test_server_host).size());
   EXPECT_EQ(embedded_test_server()->host_port_pair().host(),
             ip_map->at(test_server_host).back().ip);
   EXPECT_NE(yesterday, ip_map->at(test_server_host).front().timestamp);
diff --git a/chrome/browser/safe_browsing/safe_browsing_navigation_observer_manager.cc b/chrome/browser/safe_browsing/safe_browsing_navigation_observer_manager.cc
index 14fdb02a..989f7d61 100644
--- a/chrome/browser/safe_browsing/safe_browsing_navigation_observer_manager.cc
+++ b/chrome/browser/safe_browsing/safe_browsing_navigation_observer_manager.cc
@@ -5,7 +5,10 @@
 #include "chrome/browser/safe_browsing/safe_browsing_navigation_observer_manager.h"
 
 #include "base/memory/ptr_util.h"
+#include "base/metrics/histogram_macros.h"
+#include "base/strings/stringprintf.h"
 #include "base/time/time.h"
+#include "base/timer/timer.h"
 #include "chrome/browser/chrome_notification_types.h"
 #include "chrome/browser/safe_browsing/safe_browsing_navigation_observer.h"
 #include "chrome/browser/sessions/session_tab_helper.h"
@@ -21,20 +24,36 @@
 
 namespace safe_browsing {
 
+namespace {
+
+// Given when an event happened and its TTL, determine if it is already expired.
+// Note, if for some reason this event's timestamp is in the future, this
+// event's timestamp is invalid, hence we treat it as expired.
+bool IsEventExpired(const base::Time& event_time, double ttl_in_second) {
+  double current_time_in_second = base::Time::Now().ToDoubleT();
+  double event_time_in_second = event_time.ToDoubleT();
+  if (current_time_in_second <= event_time_in_second)
+    return true;
+  return current_time_in_second - event_time_in_second > ttl_in_second;
+}
+
+}  // namespace
+
 // The expiration period of a user gesture. Any user gesture that happened 1.0
-// second ago will be considered as expired and not relevant to upcoming
-// navigation events.
+// second ago is considered as expired and not relevant to upcoming navigation
+// events.
 static const double kUserGestureTTLInSecond = 1.0;
+// The expiration period of navigation events and resolved IP addresses. Any
+// navigation related records that happened 2 minutes ago are considered as
+// expired. So we clean up these navigation footprints every 2 minutes.
+static const double kNavigationFootprintTTLInSecond = 120.0;
+// The number of user gestures we trace back for download attribution.
+static const int kDownloadAttributionUserGestureLimit = 2;
 
 // static
 bool SafeBrowsingNavigationObserverManager::IsUserGestureExpired(
     const base::Time& timestamp) {
-  double now = base::Time::Now().ToDoubleT();
-  double timestamp_in_double = timestamp.ToDoubleT();
-
-  if (now <= timestamp_in_double)
-    return true;
-  return (now - timestamp_in_double) > kUserGestureTTLInSecond;
+  return IsEventExpired(timestamp, kUserGestureTTLInSecond);
 }
 
 // static
@@ -50,6 +69,9 @@
 SafeBrowsingNavigationObserverManager::SafeBrowsingNavigationObserverManager() {
   registrar_.Add(this, chrome::NOTIFICATION_RETARGETING,
                  content::NotificationService::AllSources());
+
+  // TODO(jialiul): call ScheduleNextCleanUpAfterInterval() when this class is
+  // ready to be hooked into SafeBrowsingService.
 }
 
 void SafeBrowsingNavigationObserverManager::RecordNavigationEvent(
@@ -104,7 +126,109 @@
 void SafeBrowsingNavigationObserverManager::OnWebContentDestroyed(
     content::WebContents* web_contents) {
   user_gesture_map_.erase(web_contents);
-  // TODO (jialiul): Will add other clean up tasks shortly.
+}
+
+void SafeBrowsingNavigationObserverManager::CleanUpStaleNavigationFootprints() {
+  CleanUpNavigationEvents();
+  CleanUpUserGestures();
+  CleanUpIpAddresses();
+  ScheduleNextCleanUpAfterInterval(
+      base::TimeDelta::FromSecondsD(kNavigationFootprintTTLInSecond));
+}
+
+SafeBrowsingNavigationObserverManager::AttributionResult
+SafeBrowsingNavigationObserverManager::IdentifyReferrerChain(
+    const GURL& target_url,
+    int target_tab_id,
+    int user_gesture_count_limit,
+    std::vector<ReferrerChainEntry>* out_referrer_chain) {
+  if (!target_url.is_valid())
+    return INVALID_URL;
+
+  NavigationEvent* nav_event = FindNavigationEvent(target_url, target_tab_id);
+  if (!nav_event) {
+    // We cannot find a single navigation event related to this download.
+    return NAVIGATION_EVENT_NOT_FOUND;
+  }
+
+  AddToReferrerChain(out_referrer_chain, nav_event,
+                     ReferrerChainEntry::DOWNLOAD_URL);
+  AttributionResult result = SUCCESS;
+  int user_gesture_count = 0;
+  while (user_gesture_count < user_gesture_count_limit) {
+    // Back trace to the next nav_event that was initiated by the user.
+    while (!nav_event->is_user_initiated) {
+      nav_event =
+          FindNavigationEvent(nav_event->source_url, nav_event->source_tab_id);
+      if (!nav_event)
+        return result;
+      AddToReferrerChain(out_referrer_chain, nav_event,
+                         nav_event->has_server_redirect
+                             ? ReferrerChainEntry::SERVER_REDIRECT
+                             : ReferrerChainEntry::CLIENT_REDIRECT);
+    }
+
+    user_gesture_count++;
+
+    // If the source_url and source_main_frame_url of current navigation event
+    // are empty, and is_user_initiated is true, this is a browser initiated
+    // navigation (e.g. trigged by typing in address bar, clicking on bookmark,
+    // etc). We reached the end of the referrer chain.
+    if (nav_event->source_url.is_empty() &&
+        nav_event->source_main_frame_url.is_empty()) {
+      DCHECK(nav_event->is_user_initiated);
+      return result;
+    }
+
+    nav_event =
+        FindNavigationEvent(nav_event->source_url, nav_event->source_tab_id);
+    if (!nav_event)
+      return result;
+
+    // Landing page of a download refers to the page user directly interacts
+    // with to trigger this download (e.g. clicking on download button). Landing
+    // referrer page is the one user interacts with right before navigating to
+    // the landing page.
+    // Since we are tracing navigations backwards, if we've encountered 1 user
+    // gesture before this navigation event, this is a navigation leading to the
+    // landing page. If we've encountered 2 user gestures, it leads to landing
+    // referrer page.
+    if (user_gesture_count == 1) {
+      AddToReferrerChain(out_referrer_chain, nav_event,
+                         ReferrerChainEntry::LANDING_PAGE);
+      result = SUCCESS_LANDING_PAGE;
+    } else if (user_gesture_count == 2) {
+      AddToReferrerChain(out_referrer_chain, nav_event,
+                         ReferrerChainEntry::LANDING_REFERRER);
+      result = SUCCESS_LANDING_REFERRER;
+    } else {
+      NOTREACHED();
+    }
+  }
+  return result;
+}
+
+void SafeBrowsingNavigationObserverManager::
+    AddReferrerChainToClientDownloadRequest(
+        const GURL& download_url,
+        content::WebContents* source_contents,
+        ClientDownloadRequest* out_request) {
+  int download_tab_id = SessionTabHelper::IdForTab(source_contents);
+  UMA_HISTOGRAM_BOOLEAN(
+      "SafeBrowsing.ReferrerHasInvalidTabID.DownloadAttribution",
+      download_tab_id == -1);
+  std::vector<ReferrerChainEntry> attribution_chain;
+  AttributionResult result = IdentifyReferrerChain(
+      download_url, download_tab_id, kDownloadAttributionUserGestureLimit,
+      &attribution_chain);
+  UMA_HISTOGRAM_COUNTS_100(
+      "SafeBrowsing.ReferrerURLChainSize.DownloadAttribution",
+      attribution_chain.size());
+  UMA_HISTOGRAM_ENUMERATION(
+      "SafeBrowsing.ReferrerAttributionResult.DownloadAttribution", result,
+      SafeBrowsingNavigationObserverManager::ATTRIBUTION_FAILURE_TYPE_MAX);
+  for (auto entry : attribution_chain)
+    *out_request->add_referrer_chain() = entry;
 }
 
 SafeBrowsingNavigationObserverManager::
@@ -166,4 +290,115 @@
   insertion_result.first->second.push_back(std::move(nav_event));
 }
 
+void SafeBrowsingNavigationObserverManager::CleanUpNavigationEvents() {
+  // Remove any stale NavigationEnvent, if it is older than
+  // kNavigationFootprintTTLInSecond.
+  for (auto it = navigation_map_.begin(); it != navigation_map_.end();) {
+    it->second.erase(std::remove_if(it->second.begin(), it->second.end(),
+                                    [](const NavigationEvent& nav_event) {
+                                      return IsEventExpired(
+                                          nav_event.last_updated,
+                                          kNavigationFootprintTTLInSecond);
+                                    }),
+                     it->second.end());
+    if (it->second.size() == 0)
+      it = navigation_map_.erase(it);
+    else
+      ++it;
+  }
+}
+
+void SafeBrowsingNavigationObserverManager::CleanUpUserGestures() {
+  for (auto it = user_gesture_map_.begin(); it != user_gesture_map_.end();) {
+    if (IsEventExpired(it->second, kUserGestureTTLInSecond))
+      it = user_gesture_map_.erase(it);
+    else
+      ++it;
+  }
+}
+
+void SafeBrowsingNavigationObserverManager::CleanUpIpAddresses() {
+  for (auto it = host_to_ip_map_.begin(); it != host_to_ip_map_.end();) {
+    it->second.erase(std::remove_if(it->second.begin(), it->second.end(),
+                                    [](const ResolvedIPAddress& resolved_ip) {
+                                      return IsEventExpired(
+                                          resolved_ip.timestamp,
+                                          kNavigationFootprintTTLInSecond);
+                                    }),
+                     it->second.end());
+    if (it->second.size() == 0)
+      it = host_to_ip_map_.erase(it);
+    else
+      ++it;
+  }
+}
+
+bool SafeBrowsingNavigationObserverManager::IsCleanUpScheduled() const {
+  return cleanup_timer_.IsRunning();
+}
+
+void SafeBrowsingNavigationObserverManager::ScheduleNextCleanUpAfterInterval(
+    base::TimeDelta interval) {
+  DCHECK_GT(interval, base::TimeDelta());
+  cleanup_timer_.Stop();
+  cleanup_timer_.Start(
+      FROM_HERE, interval, this,
+      &SafeBrowsingNavigationObserverManager::CleanUpStaleNavigationFootprints);
+}
+
+NavigationEvent* SafeBrowsingNavigationObserverManager::FindNavigationEvent(
+    const GURL& target_url,
+    int target_tab_id) {
+  auto it = navigation_map_.find(target_url);
+  if (it == navigation_map_.end()) {
+    return nullptr;
+  }
+  // Since navigation events are recorded in chronological order, we traverse
+  // the vector in reverse order to get the latest match.
+  for (auto rit = it->second.rbegin(); rit != it->second.rend(); ++rit) {
+    // If tab id is not valid, we only compare url, otherwise we compare both.
+    if (rit->destination_url == target_url &&
+        (target_tab_id == -1 || rit->target_tab_id == target_tab_id)) {
+      // If both source_url and source_main_frame_url are empty, and this
+      // navigation is not triggered by user, a retargeting navigation probably
+      // causes this navigation. In this case, we skip this navigation event and
+      // looks for the retargeting navigation event.
+      if (rit->source_url.is_empty() && rit->source_main_frame_url.is_empty() &&
+          !rit->is_user_initiated) {
+        continue;
+      } else {
+        return &*rit;
+      }
+    }
+  }
+  return nullptr;
+}
+
+void SafeBrowsingNavigationObserverManager::AddToReferrerChain(
+    std::vector<ReferrerChainEntry>* referrer_chain,
+    NavigationEvent* nav_event,
+    ReferrerChainEntry::URLType type) {
+  ReferrerChainEntry referrer_chain_entry;
+  referrer_chain_entry.set_url(nav_event->destination_url.spec());
+  referrer_chain_entry.set_type(type);
+  auto ip_it = host_to_ip_map_.find(nav_event->destination_url.host());
+  if (ip_it != host_to_ip_map_.end()) {
+    for (ResolvedIPAddress entry : ip_it->second) {
+      referrer_chain_entry.add_ip_addresses(entry.ip);
+    }
+  }
+  // Since we only track navigation to landing referrer, we will not log the
+  // referrer of the landing referrer page.
+  if (type != ReferrerChainEntry::LANDING_REFERRER) {
+    referrer_chain_entry.set_referrer_url(nav_event->source_url.spec());
+    referrer_chain_entry.set_referrer_main_frame_url(
+        nav_event->source_main_frame_url.spec());
+  }
+  referrer_chain_entry.set_is_retargeting(nav_event->source_tab_id !=
+                                          nav_event->target_tab_id);
+  referrer_chain_entry.set_navigation_time_msec(
+      nav_event->last_updated.ToJavaTime());
+  referrer_chain->push_back(referrer_chain_entry);
+}
+
 }  // namespace safe_browsing
diff --git a/chrome/browser/safe_browsing/safe_browsing_navigation_observer_manager.h b/chrome/browser/safe_browsing/safe_browsing_navigation_observer_manager.h
index f66172fe..7a47da8 100644
--- a/chrome/browser/safe_browsing/safe_browsing_navigation_observer_manager.h
+++ b/chrome/browser/safe_browsing/safe_browsing_navigation_observer_manager.h
@@ -5,6 +5,7 @@
 #ifndef CHROME_BROWSER_SAFE_BROWSING_SAFE_BROWSING_NAVIGATION_OBSERVER_MANAGER_H_
 #define CHROME_BROWSER_SAFE_BROWSING_SAFE_BROWSING_NAVIGATION_OBSERVER_MANAGER_H_
 
+#include "chrome/common/safe_browsing/csd.pb.h"
 #include "content/public/browser/notification_observer.h"
 #include "content/public/browser/notification_registrar.h"
 #include "content/public/browser/web_contents_observer.h"
@@ -17,7 +18,7 @@
 struct ResolvedIPAddress;
 
 // Manager class for SafeBrowsingNavigationObserver, which is in charge of
-// cleaning up stale navigation events, and identifing landing page/landing
+// cleaning up stale navigation events, and identifying landing page/landing
 // referrer for a specific download.
 // TODO(jialiul): For now, SafeBrowsingNavigationObserverManager also listens to
 // NOTIFICATION_RETARGETING as a way to detect cross frame/tab navigation.
@@ -27,6 +28,18 @@
     : public content::NotificationObserver,
       public base::RefCountedThreadSafe<SafeBrowsingNavigationObserverManager> {
  public:
+  // For UMA histogram counting. Do NOT change order.
+  enum AttributionResult {
+    SUCCESS = 1,                   // Identified referrer chain is not empty.
+    SUCCESS_LANDING_PAGE = 2,      // Successfully identified landing page.
+    SUCCESS_LANDING_REFERRER = 3,  // Successfully identified landing referrer.
+    INVALID_URL = 4,
+    NAVIGATION_EVENT_NOT_FOUND = 5,
+
+    // Always at the end.
+    ATTRIBUTION_FAILURE_TYPE_MAX
+  };
+
   // Helper function to check if user gesture is older than
   // kUserGestureTTLInSecond.
   static bool IsUserGestureExpired(const base::Time& timestamp);
@@ -47,10 +60,34 @@
   void OnUserGestureConsumed(content::WebContents* web_contents,
                              const base::Time& timestamp);
   void RecordHostToIpMapping(const std::string& host, const std::string& ip);
+
   // Clean-ups need to be done when a WebContents gets destroyed.
   void OnWebContentDestroyed(content::WebContents* web_contents);
 
-  // TODO(jialiul): more functions are coming for managing navigation_map_.
+  // Remove all the observed NavigationEvents, user gestures, and resolved IP
+  // addresses that are older than kNavigationFootprintTTLInSecond.
+  void CleanUpStaleNavigationFootprints();
+
+  // Based on the |target_url| and |target_tab_id|, trace back the observed
+  // NavigationEvents in navigation_map_ to identify the sequence of navigations
+  // leading to the target, with the coverage limited to
+  // |user_gesture_count_limit| number of user gestures. Then convert these
+  // identified NavigationEvents into ReferrerChainEntrys and append them to
+  // |out_referrer_chain|.
+  AttributionResult IdentifyReferrerChain(
+      const GURL& target_url,
+      int target_tab_id,  // -1 if tab id is not valid
+      int user_gesture_count_limit,
+      std::vector<ReferrerChainEntry>* out_referrer_chain);
+
+  // Identify and add referrer chain info of a download to ClientDownloadRequest
+  // proto. This function also record UMA stats of download attribution result.
+  // TODO(jialiul): This function will be moved to DownloadProtectionService
+  // class shortly.
+  void AddReferrerChainToClientDownloadRequest(
+      const GURL& download_url,
+      content::WebContents* source_contents,
+      ClientDownloadRequest* out_request);
 
  private:
   friend class base::RefCountedThreadSafe<
@@ -84,6 +121,45 @@
 
   HostToIpMap* host_to_ip_map() { return &host_to_ip_map_; }
 
+  // Remove stale entries from navigation_map_ if they are older than
+  // kNavigationFootprintTTLInSecond (2 minutes).
+  void CleanUpNavigationEvents();
+
+  // Remove stale entries from user_gesture_map_ if they are older than
+  // kUserGestureTTLInSecond (1 sec).
+  void CleanUpUserGestures();
+
+  // Remove stale entries from host_to_ip_map_ if they are older than
+  // kNavigationFootprintTTLInSecond (2 minutes).
+  void CleanUpIpAddresses();
+
+  bool IsCleanUpScheduled() const;
+
+  void ScheduleNextCleanUpAfterInterval(base::TimeDelta interval);
+
+  // Find the most recent navigation event that navigated to |target_url| in the
+  // tab with ID |target_tab_id|. If |target_tab_id| is not available (-1), we
+  // look for all tabs for the most recent navigation to |target_url|.
+  // For some cases, the most recent navigation to |target_url| may not be
+  // relevant.
+  // For example, url1 in window A opens url2 in window B, url1 then opens an
+  // about:blank page window C and injects script code in it to trigger a
+  // delayed download in Window D. Before the download occurs, url2 in window B
+  // opens a different about:blank page in window C.
+  // A ---- C - D
+  //   \   /
+  //     B
+  // In this case, FindNavigationEvent() will think url2 in Window B is the
+  // referrer of about::blank in Window C since this navigation is more recent.
+  // However, it does not prevent us to attribute url1 in Window A as the cause
+  // of all these navigations.
+  NavigationEvent* FindNavigationEvent(const GURL& target_url,
+                                       int target_tab_id);
+
+  void AddToReferrerChain(std::vector<ReferrerChainEntry>* referrer_chain,
+                          NavigationEvent* nav_event,
+                          ReferrerChainEntry::URLType type);
+
   // navigation_map_ keeps track of all the observed navigations. This map is
   // keyed on the resolved request url. In other words, in case of server
   // redirects, its key is the last server redirect url, otherwise, it is the
@@ -108,6 +184,8 @@
 
   content::NotificationRegistrar registrar_;
 
+  base::OneShotTimer cleanup_timer_;
+
   DISALLOW_COPY_AND_ASSIGN(SafeBrowsingNavigationObserverManager);
 };
 }  // namespace safe_browsing
diff --git a/chrome/browser/safe_browsing/safe_browsing_navigation_observer_unittest.cc b/chrome/browser/safe_browsing/safe_browsing_navigation_observer_unittest.cc
index 3c4fde5..f38dcf8e 100644
--- a/chrome/browser/safe_browsing/safe_browsing_navigation_observer_unittest.cc
+++ b/chrome/browser/safe_browsing/safe_browsing_navigation_observer_unittest.cc
@@ -56,6 +56,34 @@
     return navigation_observer_manager_->navigation_map();
   }
 
+  SafeBrowsingNavigationObserverManager::UserGestureMap* user_gesture_map() {
+    return &navigation_observer_manager_->user_gesture_map_;
+  }
+
+  SafeBrowsingNavigationObserverManager::HostToIpMap* host_to_ip_map() {
+    return &navigation_observer_manager_->host_to_ip_map_;
+  }
+
+  NavigationEvent CreateNavigationEvent(const GURL& destination_url,
+                                        const base::Time& timestamp) {
+    NavigationEvent nav_event;
+    nav_event.destination_url = destination_url;
+    nav_event.last_updated = timestamp;
+    return nav_event;
+  }
+
+  void CleanUpNavigationEvents() {
+    navigation_observer_manager_->CleanUpNavigationEvents();
+  }
+
+  void CleanUpIpAddresses() {
+    navigation_observer_manager_->CleanUpIpAddresses();
+  }
+
+  void CleanUpUserGestures() {
+    navigation_observer_manager_->CleanUpUserGestures();
+  }
+
  protected:
   SafeBrowsingNavigationObserverManager* navigation_observer_manager_;
   SafeBrowsingNavigationObserver* navigation_observer_;
@@ -77,8 +105,8 @@
   auto nav_map = navigation_map();
   ASSERT_EQ(std::size_t(1), nav_map->size());
   ASSERT_EQ(std::size_t(1), nav_map->at(GURL("http://foo/1")).size());
-  VerifyNavigationEvent(GURL("http://foo/0"),  // source_url
-                        GURL("http://foo/0"),  // source_main_frame_url
+  VerifyNavigationEvent(GURL(),                // source_url
+                        GURL(),                // source_main_frame_url
                         GURL("http://foo/1"),  // original_request_url
                         GURL("http://foo/1"),  // destination_url
                         tab_id,                // source_tab_id
@@ -114,4 +142,105 @@
                         nav_map->at(GURL("http://redirect/1")).at(0));
 }
 
+TEST_F(SBNavigationObserverTest, TestCleanUpStaleNavigationEvents) {
+  // Sets up navigation_map() such that it includes fresh, stale and invalid
+  // navigation events.
+  base::Time now = base::Time::Now();  // Fresh
+  base::Time one_hour_ago =
+      base::Time::FromDoubleT(now.ToDoubleT() - 60.0 * 60.0);  // Stale
+  base::Time one_minute_ago =
+      base::Time::FromDoubleT(now.ToDoubleT() - 60.0);  // Fresh
+  base::Time in_an_hour =
+      base::Time::FromDoubleT(now.ToDoubleT() + 60.0 * 60.0);  // Invalid
+  GURL url_0("http://foo/0");
+  GURL url_1("http://foo/1");
+  navigation_map()->insert(
+      std::make_pair(url_0, std::vector<NavigationEvent>()));
+  navigation_map()->at(url_0).push_back(
+      CreateNavigationEvent(url_0, one_hour_ago));
+  navigation_map()->at(url_0).push_back(CreateNavigationEvent(url_0, now));
+  navigation_map()->at(url_0).push_back(
+      CreateNavigationEvent(url_0, one_minute_ago));
+  navigation_map()->at(url_0).push_back(
+      CreateNavigationEvent(url_0, in_an_hour));
+  navigation_map()->insert(
+      std::make_pair(url_1, std::vector<NavigationEvent>()));
+  navigation_map()->at(url_1).push_back(
+      CreateNavigationEvent(url_0, one_hour_ago));
+  navigation_map()->at(url_1).push_back(
+      CreateNavigationEvent(url_0, one_hour_ago));
+  ASSERT_EQ(std::size_t(2), navigation_map()->size());
+  ASSERT_EQ(std::size_t(4), navigation_map()->at(url_0).size());
+  ASSERT_EQ(std::size_t(2), navigation_map()->at(url_1).size());
+
+  // Cleans up navigation events.
+  CleanUpNavigationEvents();
+
+  // Verifies all stale and invalid navigation events are removed.
+  ASSERT_EQ(std::size_t(1), navigation_map()->size());
+  EXPECT_EQ(navigation_map()->end(), navigation_map()->find(url_1));
+  EXPECT_EQ(std::size_t(2), navigation_map()->at(url_0).size());
+}
+
+TEST_F(SBNavigationObserverTest, TestCleanUpStaleUserGestures) {
+  // Sets up user_gesture_map() such that it includes fresh, stale and invalid
+  // user gestures.
+  base::Time now = base::Time::Now();  // Fresh
+  base::Time one_minute_ago =
+      base::Time::FromDoubleT(now.ToDoubleT() - 60.0);  // Stale
+  base::Time in_an_hour =
+      base::Time::FromDoubleT(now.ToDoubleT() + 60.0 * 60.0);  // Invalid
+  AddTab(browser(), GURL("http://foo/1"));
+  AddTab(browser(), GURL("http://foo/2"));
+  content::WebContents* content0 =
+      browser()->tab_strip_model()->GetWebContentsAt(0);
+  content::WebContents* content1 =
+      browser()->tab_strip_model()->GetWebContentsAt(1);
+  content::WebContents* content2 =
+      browser()->tab_strip_model()->GetWebContentsAt(2);
+  user_gesture_map()->insert(std::make_pair(content0, now));
+  user_gesture_map()->insert(std::make_pair(content1, one_minute_ago));
+  user_gesture_map()->insert(std::make_pair(content2, in_an_hour));
+  ASSERT_EQ(std::size_t(3), user_gesture_map()->size());
+
+  // Cleans up user_gesture_map()
+  CleanUpUserGestures();
+
+  // Verifies all stale and invalid user gestures are removed.
+  ASSERT_EQ(std::size_t(1), user_gesture_map()->size());
+  EXPECT_NE(user_gesture_map()->end(), user_gesture_map()->find(content0));
+  EXPECT_EQ(now, user_gesture_map()->at(content0));
+}
+
+TEST_F(SBNavigationObserverTest, TestCleanUpStaleIPAddresses) {
+  // Sets up host_to_ip_map() such that it includes fresh, stale and invalid
+  // user gestures.
+  base::Time now = base::Time::Now();  // Fresh
+  base::Time one_hour_ago =
+      base::Time::FromDoubleT(now.ToDoubleT() - 60.0 * 60.0);  // Stale
+  base::Time in_an_hour =
+      base::Time::FromDoubleT(now.ToDoubleT() + 60.0 * 60.0);  // Invalid
+  std::string host_0 = GURL("http://foo/0").host();
+  std::string host_1 = GURL("http://bar/1").host();
+  host_to_ip_map()->insert(
+      std::make_pair(host_0, std::vector<ResolvedIPAddress>()));
+  host_to_ip_map()->at(host_0).push_back(ResolvedIPAddress(now, "1.1.1.1"));
+  host_to_ip_map()->at(host_0).push_back(
+      ResolvedIPAddress(one_hour_ago, "2.2.2.2"));
+  host_to_ip_map()->insert(
+      std::make_pair(host_1, std::vector<ResolvedIPAddress>()));
+  host_to_ip_map()->at(host_1).push_back(
+      ResolvedIPAddress(in_an_hour, "3.3.3.3"));
+  ASSERT_EQ(std::size_t(2), host_to_ip_map()->size());
+
+  // Cleans up host_to_ip_map()
+  CleanUpIpAddresses();
+
+  // Verifies all stale and invalid IP addresses are removed.
+  ASSERT_EQ(std::size_t(1), host_to_ip_map()->size());
+  EXPECT_EQ(host_to_ip_map()->end(), host_to_ip_map()->find(host_1));
+  ASSERT_EQ(std::size_t(1), host_to_ip_map()->at(host_0).size());
+  EXPECT_EQ(now, host_to_ip_map()->at(host_0).front().timestamp);
+}
+
 }  // namespace safe_browsing
diff --git a/chrome/browser/sync_file_system/local/canned_syncable_file_system.cc b/chrome/browser/sync_file_system/local/canned_syncable_file_system.cc
index 73d9b608..cbac4c7 100644
--- a/chrome/browser/sync_file_system/local/canned_syncable_file_system.cc
+++ b/chrome/browser/sync_file_system/local/canned_syncable_file_system.cc
@@ -242,7 +242,8 @@
   if (quota_mode == QUOTA_ENABLED) {
     quota_manager_ = new QuotaManager(
         false /* is_incognito */, data_dir_.GetPath(), io_task_runner_.get(),
-        base::ThreadTaskRunnerHandle::Get().get(), storage_policy.get());
+        base::ThreadTaskRunnerHandle::Get().get(), storage_policy.get(),
+        storage::GetQuotaSettingsFunc());
   }
 
   std::vector<std::string> additional_allowed_schemes;
diff --git a/chrome/browser/ui/views/critical_notification_bubble_view.cc b/chrome/browser/ui/views/critical_notification_bubble_view.cc
index cbaf22be..2e26666 100644
--- a/chrome/browser/ui/views/critical_notification_bubble_view.cc
+++ b/chrome/browser/ui/views/critical_notification_bubble_view.cc
@@ -88,15 +88,6 @@
                            IDS_CRITICAL_NOTIFICATION_HEADLINE_ALTERNATE);
 }
 
-gfx::ImageSkia CriticalNotificationBubbleView::GetWindowIcon() {
-  return *ui::ResourceBundle::GetSharedInstance().GetImageSkiaNamed(
-      IDR_UPDATE_MENU_SEVERITY_HIGH);
-}
-
-bool CriticalNotificationBubbleView::ShouldShowWindowIcon() const {
-  return true;
-}
-
 void CriticalNotificationBubbleView::WindowClosing() {
   refresh_timer_.Stop();
 }
diff --git a/chrome/browser/ui/views/critical_notification_bubble_view.h b/chrome/browser/ui/views/critical_notification_bubble_view.h
index 3cd0cc2..badc0cf 100644
--- a/chrome/browser/ui/views/critical_notification_bubble_view.h
+++ b/chrome/browser/ui/views/critical_notification_bubble_view.h
@@ -16,8 +16,6 @@
 
   // views::BubbleDialogDelegateView overrides:
   base::string16 GetWindowTitle() const override;
-  gfx::ImageSkia GetWindowIcon() override;
-  bool ShouldShowWindowIcon() const override;
   void WindowClosing() override;
   bool Cancel() override;
   bool Accept() override;
diff --git a/chrome/browser/ui/views/outdated_upgrade_bubble_view.cc b/chrome/browser/ui/views/outdated_upgrade_bubble_view.cc
index 1df5677..e32de0d 100644
--- a/chrome/browser/ui/views/outdated_upgrade_bubble_view.cc
+++ b/chrome/browser/ui/views/outdated_upgrade_bubble_view.cc
@@ -13,13 +13,11 @@
 #include "chrome/common/pref_names.h"
 #include "chrome/grit/chromium_strings.h"
 #include "chrome/grit/generated_resources.h"
-#include "chrome/grit/theme_resources.h"
 #include "components/prefs/pref_service.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/page_navigator.h"
 #include "content/public/browser/user_metrics.h"
 #include "ui/base/l10n/l10n_util.h"
-#include "ui/base/resource/resource_bundle.h"
 #include "ui/views/controls/label.h"
 #include "ui/views/layout/fill_layout.h"
 #include "ui/views/layout/layout_constants.h"
@@ -100,15 +98,6 @@
   return l10n_util::GetStringUTF16(IDS_UPGRADE_BUBBLE_TITLE);
 }
 
-gfx::ImageSkia OutdatedUpgradeBubbleView::GetWindowIcon() {
-  return *ui::ResourceBundle::GetSharedInstance().GetImageSkiaNamed(
-      IDR_UPDATE_MENU_SEVERITY_HIGH);
-}
-
-bool OutdatedUpgradeBubbleView::ShouldShowWindowIcon() const {
-  return true;
-}
-
 bool OutdatedUpgradeBubbleView::Cancel() {
   content::RecordAction(base::UserMetricsAction("OutdatedUpgradeBubble.Later"));
   return true;
diff --git a/chrome/browser/ui/views/outdated_upgrade_bubble_view.h b/chrome/browser/ui/views/outdated_upgrade_bubble_view.h
index dcfc9c9..915e4e7 100644
--- a/chrome/browser/ui/views/outdated_upgrade_bubble_view.h
+++ b/chrome/browser/ui/views/outdated_upgrade_bubble_view.h
@@ -31,8 +31,6 @@
   // views::BubbleDialogDelegateView methods.
   void WindowClosing() override;
   base::string16 GetWindowTitle() const override;
-  gfx::ImageSkia GetWindowIcon() override;
-  bool ShouldShowWindowIcon() const override;
   bool Cancel() override;
   bool Accept() override;
   void UpdateButton(views::LabelButton* button, ui::DialogButton type) override;
diff --git a/chrome/browser/ui/webui/quota_internals/quota_internals_proxy.cc b/chrome/browser/ui/webui/quota_internals/quota_internals_proxy.cc
index 11decea0..2e88070bc 100644
--- a/chrome/browser/ui/webui/quota_internals/quota_internals_proxy.cc
+++ b/chrome/browser/ui/webui/quota_internals/quota_internals_proxy.cc
@@ -32,19 +32,12 @@
     return;
   }
   quota_manager_ = quota_manager;
-  {
-    // crbug.com/349708
-    TRACE_EVENT0("io", "QuotaInternalsProxy::RequestInfo");
 
-    quota_manager_->GetAvailableSpace(
-        base::Bind(&QuotaInternalsProxy::DidGetAvailableSpace,
-                   weak_factory_.GetWeakPtr()));
-  }
+  quota_manager_->GetQuotaSettings(base::Bind(
+      &QuotaInternalsProxy::DidGetSettings, weak_factory_.GetWeakPtr()));
 
-  quota_manager_->GetTemporaryGlobalQuota(
-      base::Bind(&QuotaInternalsProxy::DidGetGlobalQuota,
-                 weak_factory_.GetWeakPtr(),
-                 storage::kStorageTypeTemporary));
+  quota_manager_->GetStorageCapacity(base::Bind(
+      &QuotaInternalsProxy::DidGetCapacity, weak_factory_.GetWeakPtr()));
 
   quota_manager_->GetGlobalUsage(
       storage::kStorageTypeTemporary,
@@ -101,23 +94,18 @@
 
 #undef RELAY_TO_HANDLER
 
-void QuotaInternalsProxy::DidGetAvailableSpace(storage::QuotaStatusCode status,
-                                               int64_t space) {
-  // crbug.com/349708
-  TRACE_EVENT0("io", "QuotaInternalsProxy::DidGetAvailableSpace");
-
-  if (status == storage::kQuotaStatusOk)
-    ReportAvailableSpace(space);
+void QuotaInternalsProxy::DidGetSettings(
+    const storage::QuotaSettings& settings) {
+  // TODO(michaeln): also report the other config fields
+  GlobalStorageInfo info(storage::kStorageTypeTemporary);
+  info.set_quota(settings.pool_size);
+  ReportGlobalInfo(info);
 }
 
-void QuotaInternalsProxy::DidGetGlobalQuota(storage::StorageType type,
-                                            storage::QuotaStatusCode status,
-                                            int64_t quota) {
-  if (status == storage::kQuotaStatusOk) {
-    GlobalStorageInfo info(type);
-    info.set_quota(quota);
-    ReportGlobalInfo(info);
-  }
+void QuotaInternalsProxy::DidGetCapacity(int64_t total_space,
+                                         int64_t available_space) {
+  // TODO(michaeln): also report total_space
+  ReportAvailableSpace(available_space);
 }
 
 void QuotaInternalsProxy::DidGetGlobalUsage(storage::StorageType type,
diff --git a/chrome/browser/ui/webui/quota_internals/quota_internals_proxy.h b/chrome/browser/ui/webui/quota_internals/quota_internals_proxy.h
index b3e3d661..c8e750f 100644
--- a/chrome/browser/ui/webui/quota_internals/quota_internals_proxy.h
+++ b/chrome/browser/ui/webui/quota_internals/quota_internals_proxy.h
@@ -58,10 +58,8 @@
   void ReportStatistics(const Statistics& stats);
 
   // Called on IO Thread by QuotaManager as callback.
-  void DidGetAvailableSpace(storage::QuotaStatusCode status, int64_t space);
-  void DidGetGlobalQuota(storage::StorageType type,
-                         storage::QuotaStatusCode status,
-                         int64_t quota);
+  void DidGetSettings(const storage::QuotaSettings& settings);
+  void DidGetCapacity(int64_t total_space, int64_t available_space);
   void DidGetGlobalUsage(storage::StorageType type,
                          int64_t usage,
                          int64_t unlimited_usage);
diff --git a/chrome/browser/webshare/OWNERS b/chrome/browser/webshare/OWNERS
new file mode 100644
index 0000000..bba345d
--- /dev/null
+++ b/chrome/browser/webshare/OWNERS
@@ -0,0 +1,2 @@
+mgiuca@chromium.com
+sammc@chromium.com
diff --git a/chrome/browser/webshare/share_service_impl.cc b/chrome/browser/webshare/share_service_impl.cc
new file mode 100644
index 0000000..9f8bb0b
--- /dev/null
+++ b/chrome/browser/webshare/share_service_impl.cc
@@ -0,0 +1,22 @@
+// 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.
+
+#include "chrome/browser/webshare/share_service_impl.h"
+
+#include "mojo/public/cpp/bindings/strong_binding.h"
+
+// static
+void ShareServiceImpl::Create(blink::mojom::ShareServiceRequest request) {
+  mojo::MakeStrongBinding(base::MakeUnique<ShareServiceImpl>(),
+                          std::move(request));
+}
+
+void ShareServiceImpl::Share(const std::string& title,
+                             const std::string& text,
+                             const GURL& url,
+                             const ShareCallback& callback) {
+  // TODO(constantina): Implement Web Share Target here.
+  NOTIMPLEMENTED();
+  callback.Run(base::Optional<std::string>("Not implemented: navigator.share"));
+}
diff --git a/chrome/browser/webshare/share_service_impl.h b/chrome/browser/webshare/share_service_impl.h
new file mode 100644
index 0000000..83de10f
--- /dev/null
+++ b/chrome/browser/webshare/share_service_impl.h
@@ -0,0 +1,32 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_WEBSHARE_SHARE_SERVICE_IMPL_H_
+#define CHROME_BROWSER_WEBSHARE_SHARE_SERVICE_IMPL_H_
+
+#include <string>
+
+#include "mojo/public/cpp/bindings/interface_request.h"
+#include "third_party/WebKit/public/platform/modules/webshare/webshare.mojom.h"
+
+class GURL;
+
+// Desktop implementation of the ShareService Mojo service.
+class ShareServiceImpl : public blink::mojom::ShareService {
+ public:
+  ShareServiceImpl() = default;
+  ~ShareServiceImpl() override = default;
+
+  static void Create(mojo::InterfaceRequest<ShareService> request);
+
+  void Share(const std::string& title,
+             const std::string& text,
+             const GURL& url,
+             const ShareCallback& callback) override;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(ShareServiceImpl);
+};
+
+#endif  // CHROME_BROWSER_WEBSHARE_SHARE_SERVICE_IMPL_H_
diff --git a/chrome/browser/webshare/share_service_impl_unittest.cc b/chrome/browser/webshare/share_service_impl_unittest.cc
new file mode 100644
index 0000000..fe0e1c7
--- /dev/null
+++ b/chrome/browser/webshare/share_service_impl_unittest.cc
@@ -0,0 +1,56 @@
+// 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.
+
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/run_loop.h"
+#include "chrome/browser/webshare/share_service_impl.h"
+#include "chrome/test/base/chrome_render_view_host_test_harness.h"
+#include "mojo/public/cpp/bindings/interface_request.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "url/gurl.h"
+
+class ShareServiceTest : public ChromeRenderViewHostTestHarness {
+ public:
+  ShareServiceTest() = default;
+  ~ShareServiceTest() override = default;
+
+  void SetUp() override {
+    ChromeRenderViewHostTestHarness::SetUp();
+
+    ShareServiceImpl::Create(mojo::GetProxy(&share_service_));
+  }
+
+  void TearDown() override {
+    ChromeRenderViewHostTestHarness::TearDown();
+  }
+
+  void DidShare(const base::Optional<std::string>& expected,
+                const base::Optional<std::string>& str) {
+    EXPECT_EQ(expected, str);
+
+    if (!on_callback_.is_null())
+      on_callback_.Run();
+  }
+
+  blink::mojom::ShareServicePtr share_service_;
+  base::Closure on_callback_;
+};
+
+// Basic test to check the Share method calls the callback with the expected
+// parameters.
+TEST_F(ShareServiceTest, ShareCallbackParams) {
+  const GURL url("https://www.google.com");
+
+  base::RunLoop run_loop;
+  on_callback_ = run_loop.QuitClosure();
+
+  base::Callback<void(const base::Optional<std::string>&)> callback =
+      base::Bind(
+          &ShareServiceTest::DidShare, base::Unretained(this),
+          base::Optional<std::string>("Not implemented: navigator.share"));
+  share_service_->Share("title", "text", url, callback);
+
+  run_loop.Run();
+}
diff --git a/chrome/common/safe_browsing/csd.proto b/chrome/common/safe_browsing/csd.proto
index ce779cae..c98152a 100644
--- a/chrome/common/safe_browsing/csd.proto
+++ b/chrome/common/safe_browsing/csd.proto
@@ -382,46 +382,42 @@
   // the leading extension separator.
   repeated string alternate_extensions = 35;
 
-  message URLChainEntry {
-    enum URLType {
-      DOWNLOAD_URL = 1;
-      DOWNLOAD_REFERRER = 2;
-      LANDING_PAGE = 3;
-      LANDING_REFERRER = 4;
-      CLIENT_REDIRECT = 5;
-      SERVER_REDIRECT = 6;
-    }
-
-    // [required] The url of this Entry.
-    optional string url = 1;
-
-    // Type of URLs, such as download url, download referrer, etc.
-    optional URLType type = 2;
-
-    // IP address corresponding to url.
-    optional string ip_address = 3;
-
-    // Referrer url of this entry.
-    optional string referrer = 4;
-
-    // Main frame URL of referrer.
-    optional string main_frame_referrer = 5;
-
-    // If this URL loads in a different tab/frame from previous one.
-    optional bool is_retargeting = 6;
-
-    // If there is a user gesture attached to this transition.
-    optional bool is_user_initiated = 7;
-
-    optional double timestamp_in_millisec = 8;
-  }  // End of URLChainEntry
-
   // URLs transitions from landing referrer to download in reverse chronological
   // order, i.e. download url comes first in this list, and landing referrer
   // comes last.
-  repeated URLChainEntry url_chain = 36;
+  repeated ReferrerChainEntry referrer_chain = 36;
 }
 
+message ReferrerChainEntry {
+  enum URLType {
+    DOWNLOAD_URL = 1;
+    LANDING_PAGE = 2;
+    LANDING_REFERRER = 3;
+    CLIENT_REDIRECT = 4;
+    SERVER_REDIRECT = 5;
+  }
+
+  // [required] The url of this Entry.
+  optional string url = 1;
+
+  // Type of URLs, such as download url, download referrer, etc.
+  optional URLType type = 2;
+
+  // IP addresses corresponding to this host.
+  repeated string ip_addresses = 3;
+
+  // Referrer url of this entry.
+  optional string referrer_url = 4;
+
+  // Main frame URL of referrer.
+  optional string referrer_main_frame_url = 5;
+
+  // If this URL loads in a different tab/frame from previous one.
+  optional bool is_retargeting = 6;
+
+  optional double navigation_time_msec = 7;
+}  // End of URLChainEntry
+
 message ClientDownloadResponse {
   enum Verdict {
     // Download is considered safe.
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index 068d9351..066be1a 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -3122,7 +3122,6 @@
     "../browser/download/download_target_determiner_unittest.cc",
     "../browser/download/download_ui_controller_unittest.cc",
     "../browser/engagement/important_sites_util_unittest.cc",
-    "../browser/engagement/site_engagement_eviction_policy_unittest.cc",
     "../browser/engagement/site_engagement_helper_unittest.cc",
     "../browser/engagement/site_engagement_score_unittest.cc",
     "../browser/engagement/site_engagement_service_unittest.cc",
@@ -3334,6 +3333,7 @@
     "../browser/ui/webui/local_state/local_state_ui_unittest.cc",
     "../browser/ui/webui/log_web_ui_url_unittest.cc",
     "../browser/update_client/chrome_update_query_params_delegate_unittest.cc",
+    "../browser/webshare/share_service_impl_unittest.cc",
     "../browser/win/chrome_elf_init_unittest.cc",
     "../browser/win/enumerate_modules_model_unittest.cc",
     "../common/chrome_content_client_unittest.cc",
diff --git a/chrome/test/DEPS b/chrome/test/DEPS
index a975b8f..c65a276 100644
--- a/chrome/test/DEPS
+++ b/chrome/test/DEPS
@@ -13,6 +13,8 @@
   "+mojo",
   "+rlz/features",
   "+services",
+  "+storage/browser",
+  "+storage/common",
 
   # Tests under chrome/ shouldn't need to access the internals of content/ and
   # as such are allowed only content/public. If you find yourself wanting to
diff --git a/chrome/test/base/in_process_browser_test.cc b/chrome/test/base/in_process_browser_test.cc
index 2f8ecb4..48ac9eea 100644
--- a/chrome/test/base/in_process_browser_test.cc
+++ b/chrome/test/base/in_process_browser_test.cc
@@ -22,6 +22,7 @@
 #include "build/build_config.h"
 #include "chrome/browser/after_startup_task_utils.h"
 #include "chrome/browser/browser_process.h"
+#include "chrome/browser/chrome_content_browser_client.h"
 #include "chrome/browser/chrome_notification_types.h"
 #include "chrome/browser/devtools/devtools_window.h"
 #include "chrome/browser/lifetime/application_lifetime.h"
@@ -248,6 +249,12 @@
 
   google_util::SetMockLinkDoctorBaseURLForTesting();
 
+  // Use hardcoded quota settings to have a consistent testing environment.
+  const int kQuota = 5 * 1024 * 1024;
+  quota_settings_ = storage::QuotaSettings(kQuota * 5, kQuota, 0);
+  ChromeContentBrowserClient::SetDefaultQuotaSettingsForTesting(
+      &quota_settings_);
+
   BrowserTestBase::SetUp();
 }
 
@@ -357,6 +364,7 @@
 #endif
   BrowserTestBase::TearDown();
   OSCryptMocker::TearDown();
+  ChromeContentBrowserClient::SetDefaultQuotaSettingsForTesting(nullptr);
 }
 
 void InProcessBrowserTest::CloseBrowserSynchronously(Browser* browser) {
diff --git a/chrome/test/base/in_process_browser_test.h b/chrome/test/base/in_process_browser_test.h
index c66260e..5dd5428 100644
--- a/chrome/test/base/in_process_browser_test.h
+++ b/chrome/test/base/in_process_browser_test.h
@@ -14,6 +14,7 @@
 #include "content/public/browser/web_contents.h"
 #include "content/public/test/browser_test.h"
 #include "content/public/test/browser_test_base.h"
+#include "storage/browser/quota/quota_settings.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/base/page_transition_types.h"
 
@@ -260,6 +261,9 @@
   // This is reset for every test case.
   bool run_accessibility_checks_for_test_case_;
 
+  // We use hardcoded quota settings to have a consistent testing environment.
+  storage::QuotaSettings quota_settings_;
+
 #if defined(OS_MACOSX)
   base::mac::ScopedNSAutoreleasePool* autorelease_pool_;
   std::unique_ptr<ScopedBundleSwizzlerMac> bundle_swizzler_;
diff --git a/chrome/test/data/safe_browsing/download_protection/navigation_observer/landing.html b/chrome/test/data/safe_browsing/download_protection/navigation_observer/landing.html
new file mode 100644
index 0000000..f4bd7d5
--- /dev/null
+++ b/chrome/test/data/safe_browsing/download_protection/navigation_observer/landing.html
@@ -0,0 +1,19 @@
+<html>
+  <head>
+    <script>
+      // Click on a link by id to start a test case.
+      function clickLink(linkId) {
+        var node = document.getElementById(linkId);
+        if (node != null) {
+          // Click and open link in the same tab.
+          node.click();
+        }
+      }
+    </script>
+  </head>
+  <body>
+    <a id="download_on_landing_page" href="../signed.exe">
+      Direct download from landing page
+    </a><br>
+  </body>
+</html>
diff --git a/chrome/test/data/safe_browsing/download_protection/navigation_observer/landing_referrer.html b/chrome/test/data/safe_browsing/download_protection/navigation_observer/landing_referrer.html
new file mode 100644
index 0000000..d7f29275
--- /dev/null
+++ b/chrome/test/data/safe_browsing/download_protection/navigation_observer/landing_referrer.html
@@ -0,0 +1,17 @@
+<html>
+  <head>
+    <script>
+      // Click on a link by id to start a test case.
+      function clickLink(linkId) {
+        var node = document.getElementById(linkId);
+        // Click and open link in the same tab.
+        node.click();
+      }
+    </script>
+  </head>
+  <body>
+    <a id="link_to_landing" href="landing.html">
+      Link to landing
+    </a><br>
+  </body>
+</html>
diff --git a/chrome/test/data/safe_browsing/download_protection/navigation_observer/navigation_observer_tests.html b/chrome/test/data/safe_browsing/download_protection/navigation_observer/navigation_observer_tests.html
index ba37365..0868814 100644
--- a/chrome/test/data/safe_browsing/download_protection/navigation_observer/navigation_observer_tests.html
+++ b/chrome/test/data/safe_browsing/download_protection/navigation_observer/navigation_observer_tests.html
@@ -26,7 +26,7 @@
       tab.document.write('<META HTTP-EQUIV="refresh" content="0; url=../signed.exe">');
       tab.document.close();
     }
-    
+
     // Trigger download in a new tab and the download is from a data url.
     function downloadInNewTabWithDataURL() {
        var tab = window.open('');
@@ -34,7 +34,7 @@
        tab.document.write('<META HTTP-EQUIV="refresh" content="0; url=data:application/octet-stream;base64,a2poYWxrc2hkbGtoYXNka2xoYXNsa2RoYWxraGtoYWxza2hka2xzamFoZGxramhhc2xka2hhc2xrZGgKYXNrZGpoa2FzZGpoYWtzaGRrYXNoZGtoYXNrZGhhc2tkaGthc2hka2Foc2RraGFrc2hka2FzaGRraGFzCmFza2pkaGFrc2hkbSxjbmtzamFoZGtoYXNrZGhhc2tka2hrYXNkCjg3MzQ2ODEyNzQ2OGtqc2hka2FoZHNrZGhraApha3NqZGthc2Roa3NkaGthc2hka2FzaGtkaAohISomXkAqJl4qYWhpZGFzeWRpeWlhc1xcb1wKa2Fqc2Roa2FzaGRrYXNoZGsKYWtzamRoc2tkaAplbmQK">');
        tab.document.close();
     }
-    
+
     // Create a data blob and save it as a test.exe file in filesystem's
     // space with use URL filesystem:http://test_host/temporary/test.exe
     // to download it.
@@ -117,6 +117,14 @@
 
     <a id="html5_file_api" href="" onclick="downloadViaFileApi()">
       Download via HTML5 file system API
-    </a>
+    </a><br>
+
+    <a id="complete_referrer_chain" href="redirect_to_landing.html">
+      Click on landing referrer and landing page then reach download
+    </a><br>
+
+    <a id="attribution_within_two_user_gestures" href="page_before_landing_referrer.html">
+      Attribution should not trace back more than 2 user gestures.
+    </a><br>
   </body>
 </html>
diff --git a/chrome/test/data/safe_browsing/download_protection/navigation_observer/page_before_landing_referrer.html b/chrome/test/data/safe_browsing/download_protection/navigation_observer/page_before_landing_referrer.html
new file mode 100644
index 0000000..1c898f7
--- /dev/null
+++ b/chrome/test/data/safe_browsing/download_protection/navigation_observer/page_before_landing_referrer.html
@@ -0,0 +1,19 @@
+<html>
+  <head>
+    <script>
+      // Click on a link by id to start a test case.
+      function clickLink(linkId) {
+        var node = document.getElementById(linkId);
+        if (node != null) {
+          // Click and open link in the same tab.
+          node.click();
+        }
+      }
+    </script>
+  </head>
+  <body>
+    <a id="link_to_landing_referrer" href="landing_referrer.html">
+      Link to landing referrer
+    </a><br>
+  </body>
+</html>
diff --git a/chrome/test/data/safe_browsing/download_protection/navigation_observer/redirect_to_landing.html b/chrome/test/data/safe_browsing/download_protection/navigation_observer/redirect_to_landing.html
new file mode 100644
index 0000000..b32b795
--- /dev/null
+++ b/chrome/test/data/safe_browsing/download_protection/navigation_observer/redirect_to_landing.html
@@ -0,0 +1,8 @@
+<html>
+  <head>
+    <META HTTP-EQUIV="refresh" content="0; url=landing.html">
+  </head>
+  <body>
+    Redirecting to landing.html.
+  </body>
+</html>
diff --git a/chrome/test/data/webui/settings/advanced_page_browsertest.js b/chrome/test/data/webui/settings/advanced_page_browsertest.js
index 5a10142..e8652d37 100644
--- a/chrome/test/data/webui/settings/advanced_page_browsertest.js
+++ b/chrome/test/data/webui/settings/advanced_page_browsertest.js
@@ -41,14 +41,14 @@
     });
 
     test('advanced pages', function() {
-      var page = self.getPage('advanced');
+      var page = self.getPage('basic');
       var sections = ['privacy', 'passwordsAndForms', 'languages', 'downloads',
           'reset'];
       if (cr.isChromeOS)
         sections = sections.concat(['dateTime', 'bluetooth', 'a11y']);
 
       for (var i = 0; i < sections.length; i++) {
-        var section = self.getSection(page, sections[i]);
+        var section = self.getSection(page, sections[i], true /* advanced */);
         expectTrue(!!section);
         self.verifySubpagesHidden(section);
       }
diff --git a/chrome/test/data/webui/settings/bluetooth_page_browsertest_chromeos.js b/chrome/test/data/webui/settings/bluetooth_page_browsertest_chromeos.js
index 9bc4ec64..3f7f6ffa3 100644
--- a/chrome/test/data/webui/settings/bluetooth_page_browsertest_chromeos.js
+++ b/chrome/test/data/webui/settings/bluetooth_page_browsertest_chromeos.js
@@ -60,9 +60,9 @@
   var self = this;
 
   self.toggleAdvanced();
-  var advanced = self.getPage('advanced');
-  assertTrue(!!advanced);
-  advanced.set('pageVisibility.bluetooth', true);
+  var page = self.getPage('basic');
+  assertTrue(!!page);
+  page.set('pageVisibility.bluetooth', true);
   Polymer.dom.flush();
 
   /** @type {!Array<!chrome.bluetooth.Device>} */ var fakeDevices_ = [
@@ -94,7 +94,7 @@
   suite('SettingsBluetoothPage', function() {
     test('enable', function() {
       assertFalse(self.bluetoothApi_.adapterState.powered);
-      var bluetoothSection = self.getSection(advanced, 'bluetooth');
+      var bluetoothSection = self.getSection(page, 'bluetooth');
       assertTrue(!!bluetoothSection);
       var bluetooth =
           bluetoothSection.querySelector('settings-bluetooth-page');
@@ -119,7 +119,7 @@
     });
 
     test('device list', function() {
-      var bluetoothSection = self.getSection(advanced, 'bluetooth');
+      var bluetoothSection = self.getSection(page, 'bluetooth');
       var bluetooth =
           bluetoothSection.querySelector('settings-bluetooth-page');
       assertTrue(!!bluetooth);
@@ -153,7 +153,7 @@
     });
 
     test('device dialog', function() {
-      var bluetoothSection = self.getSection(advanced, 'bluetooth');
+      var bluetoothSection = self.getSection(page, 'bluetooth');
       var bluetooth =
           bluetoothSection.querySelector('settings-bluetooth-page');
       assertTrue(!!bluetooth);
diff --git a/chrome/test/data/webui/settings/languages_page_browsertest.js b/chrome/test/data/webui/settings/languages_page_browsertest.js
index 8a64216e..fe801319 100644
--- a/chrome/test/data/webui/settings/languages_page_browsertest.js
+++ b/chrome/test/data/webui/settings/languages_page_browsertest.js
@@ -47,7 +47,7 @@
     testing.Test.disableAnimationsAndTransitions();
 
     this.toggleAdvanced();
-    var advanced = this.getPage('advanced');
+    var page = this.getPage('basic');
 
     var languagesSection;
     var languagesPage;
@@ -81,10 +81,10 @@
     }
 
     suiteSetup(function() {
-      advanced.set('pageVisibility.languages', true);
+      page.set('pageVisibility.languages', true);
       Polymer.dom.flush();
 
-      languagesSection = assert(this.getSection(advanced, 'languages'));
+      languagesSection = assert(this.getSection(page, 'languages'));
       languagesPage = assert(
           languagesSection.querySelector('settings-languages-page'));
       languagesCollapse = languagesPage.$.languagesCollapse;
diff --git a/chrome/test/data/webui/settings/settings_main_test.js b/chrome/test/data/webui/settings/settings_main_test.js
index 177581b8..dc271511 100644
--- a/chrome/test/data/webui/settings/settings_main_test.js
+++ b/chrome/test/data/webui/settings/settings_main_test.js
@@ -104,20 +104,40 @@
         });
       });
 
+      /** @return {!HTMLElement} */
+      function getToggleContainer() {
+        var page = settingsMain.$$('settings-basic-page');
+        assertTrue(!!page);
+        var toggleContainer = page.$$('#toggleContainer');
+        assertTrue(!!toggleContainer);
+        return toggleContainer;
+      }
+
+      /**
+       * Asserts that the Advanced toggle container exists in the combined
+       * settings page and asserts whether it should be visible.
+       * @param {boolean} expectedVisible
+       */
+      function assertToggleContainerVisible(expectedVisible) {
+        var toggleContainer = getToggleContainer();
+        if (expectedVisible)
+          assertNotEquals('none', toggleContainer.style.display);
+        else
+          assertEquals('none', toggleContainer.style.display);
+      }
+
       test('no results page shows and hides', function() {
         Polymer.dom.flush();
         var noSearchResults = settingsMain.$.noSearchResults;
         assertTrue(!!noSearchResults);
         assertTrue(noSearchResults.hidden);
 
-        var toggleContainer = settingsMain.$$('#toggleContainer');
-        assertTrue(!!toggleContainer);
-        assertNotEquals('none', toggleContainer.style.display);
+        assertToggleContainerVisible(true);
 
         searchManager.setMatchesFound(false);
         return settingsMain.searchContents('Query1').then(function() {
           assertFalse(noSearchResults.hidden);
-          assertEquals('none', toggleContainer.style.display);
+          assertToggleContainerVisible(false);
 
           searchManager.setMatchesFound(true);
           return settingsMain.searchContents('Query2');
@@ -134,20 +154,37 @@
         assertTrue(!!noSearchResults);
         assertTrue(noSearchResults.hidden);
 
-        var toggleContainer = settingsMain.$$('#toggleContainer');
-        assertTrue(!!toggleContainer);
-        assertNotEquals('none', toggleContainer.style.display);
+        assertToggleContainerVisible(true);
 
         searchManager.setMatchesFound(false);
         // Clearing the search box is effectively a search for the empty string.
         return settingsMain.searchContents('').then(function() {
           Polymer.dom.flush();
           assertTrue(noSearchResults.hidden);
-          assertNotEquals('none', toggleContainer.style.display);
+          assertToggleContainerVisible(true);
         });
       });
 
       /**
+       * Asserts the visibility of the basic and advanced pages.
+       * @param {string} Expected 'display' value for the basic page.
+       * @param {string} Expected 'display' value for the advanced page.
+       */
+      function assertPageVisibility(expectedBasic, expectedAdvanced) {
+        Polymer.dom.flush();
+        var page = settingsMain.$$('settings-basic-page');
+        assertEquals(
+            expectedBasic, page.$$('#basicPage').style.display);
+        assertEquals(
+            expectedAdvanced, page.$$('#advancedPage').style.display);
+      }
+
+      // TODO(michaelpg): It would be better not to drill into
+      // settings-basic-page. If search should indeed only work in Settings
+      // (as opposed to Advanced), perhaps some of this logic should be
+      // delegated to settings-basic-page now instead of settings-main.
+
+      /**
        * Asserts the visibility of the basic and advanced pages after exiting
        * search mode.
        * @param {string} Expected 'display' value for the basic page.
@@ -161,13 +198,7 @@
           searchManager.setMatchesFound(false);
           return settingsMain.searchContents('');
         }).then(function() {
-          Polymer.dom.flush();
-          assertEquals(
-              expectedBasic,
-              settingsMain.$$('settings-basic-page').style.display);
-          assertEquals(
-              expectedAdvanced,
-              settingsMain.$$('settings-advanced-page').style.display);
+          assertPageVisibility(expectedBasic, expectedAdvanced);
         });
       }
 
@@ -202,39 +233,36 @@
           Polymer.dom.flush();
 
           // Simulate clicking the left arrow to go back to the search results.
-          settingsMain.currentRouteChanged(settings.Route.BASIC);
-          Polymer.dom.flush();
-          assertEquals(
-              '', settingsMain.$$('settings-basic-page').style.display);
-          assertEquals(
-              '', settingsMain.$$('settings-advanced-page').style.display);
+          settings.navigateTo(settings.Route.BASIC);
+          assertPageVisibility('', '');
         });
       });
 
+      // TODO(michaelpg): Move these to a new test for settings-basic-page.
       test('can collapse advanced on advanced section route', function() {
         settings.navigateTo(settings.Route.PRIVACY);
         Polymer.dom.flush();
 
-        var advancedToggle = settingsMain.$$('#advancedToggle');
+        var advancedToggle =
+            getToggleContainer().querySelector('#advancedToggle');
         assertTrue(!!advancedToggle);
 
         MockInteractions.tap(advancedToggle);
         Polymer.dom.flush();
 
-        assertFalse(settingsMain.showPages_.advanced);
+        assertPageVisibility('', 'none');
       });
 
       test('navigating to a basic page does not collapse advanced', function() {
         settings.navigateTo(settings.Route.PRIVACY);
         Polymer.dom.flush();
 
-        var advancedToggle = settingsMain.$$('#advancedToggle');
-        assertTrue(!!advancedToggle);
+        assertToggleContainerVisible(true);
 
         settings.navigateTo(settings.Route.PEOPLE);
         Polymer.dom.flush();
 
-        assertTrue(settingsMain.showPages_.advanced);
+        assertPageVisibility('', '');
       });
     });
   }
diff --git a/chrome/test/data/webui/settings/settings_page_browsertest.js b/chrome/test/data/webui/settings/settings_page_browsertest.js
index 62e9550..a1c5ef5 100644
--- a/chrome/test/data/webui/settings/settings_page_browsertest.js
+++ b/chrome/test/data/webui/settings/settings_page_browsertest.js
@@ -46,12 +46,12 @@
   toggleAdvanced: function() {
     var settingsMain = document.querySelector('* /deep/ settings-main');
     assert(!!settingsMain);
-    settingsMain.toggleAdvancedPage_();
+    settingsMain.advancedToggleExpanded = !settingsMain.advancedToggleExpanded;
     Polymer.dom.flush();
   },
 
   /**
-   * @param {string} type The settings page type, e.g. 'advanced' or 'basic'.
+   * @param {string} type The settings page type, e.g. 'about' or 'basic'.
    * @return {!PolymerElement} The PolymerElement for the page.
    */
   getPage: function(type) {
diff --git a/chrome/test/data/webui/settings/settings_subpage_browsertest.js b/chrome/test/data/webui/settings/settings_subpage_browsertest.js
index d71eb4b..1e0e804 100644
--- a/chrome/test/data/webui/settings/settings_subpage_browsertest.js
+++ b/chrome/test/data/webui/settings/settings_subpage_browsertest.js
@@ -13,15 +13,15 @@
  * @constructor
  * @extends {SettingsPageBrowserTest}
  *
- * @param {string} pageId 'basic' or 'advanced'.
- * @param {!Array<string>} subPages
+ * @param {string} pageId Just 'basic'. TODO(michaelpg): Add 'about' if we want
+ *     to, but that requires wrapping its sole <settings-section> in a dom-if.
  */
-function SettingsSubPageBrowserTest(pageId, subPages) {
+function SettingsSubPageBrowserTest(pageId) {
   /** @type {string} */
   this.pageId = pageId;
 
   /** @type {!Array<string>} */
-  this.subPages = subPages;
+  this.subPages = [];
 }
 
 SettingsSubPageBrowserTest.prototype = {
@@ -39,25 +39,19 @@
   /** @override */
   setUp: function() {
     SettingsPageBrowserTest.prototype.setUp.call(this);
-    // Explicitly hide all of the pages (not strictly required but is more
-    // clear than relying on undefined -> hidden).
-    this.toggleAdvanced();
-    this.hideSubPages_();
+    this.verifySubPagesHidden_();
   },
 
   /*
-   * This will hide all subpages in |this.subPages|. Note: any existing subpages
-   * not listed in |this.subPages| will be shown.
+   * Checks all subpages are hidden first.
+   * @private
    */
-  hideSubPages_: function() {
+  verifySubPagesHidden_: function() {
     var page = this.getPage(this.pageId);
-    var visibility = {};
-    this.subPages.forEach(function(subPage) {
-      visibility[subPage] = false;
-    });
     assertEquals(0, Object.keys(page.pageVisibility).length);
-    page.pageVisibility = visibility;
-    // Ensure all pages are hidden.
+
+    // Ensure all pages are still hidden after the dom-ifs compute their |if|.
+    Polymer.dom.flush();
     var sections = page.shadowRoot.querySelectorAll('settings-section');
     assertTrue(!!sections);
     assertEquals(0, sections.length);
@@ -70,7 +64,7 @@
    * @param {Node} page
    * @param {string} subpage
    */
-  testPage: function(page, subPage) {
+  testSubPage: function(page, subPage) {
     Polymer.dom.flush();
     expectFalse(!!this.getSection(page, subPage));
     var startTime = window.performance.now();
@@ -85,39 +79,28 @@
   },
 
   testSubPages: function() {
-    Polymer.dom.flush();
     var page = this.getPage(this.pageId);
     this.subPages.forEach(function(subPage) {
-      if (this.includePage(subPage))
-        test(subPage, this.testPage.bind(this, page, subPage));
+      test(subPage, this.testSubPage.bind(this, page, subPage));
     }.bind(this));
   },
-
-  /**
-   * @param {string} id
-   * @return {boolean}
-   */
-  includePage: function(id) {
-    if (cr.isChromeOS)
-      return id != 'people' && id != 'defaultBrowser';
-    return id != 'internet' && id != 'users' && id != 'device' &&
-           id != 'dateTime' && id != 'bluetooth' && id != 'a11y';
-  },
 };
 
 /** @constructor @extends {SettingsSubPageBrowserTest} */
 function SettingsBasicSubPageBrowserTest() {
-  var subPages = [
+  SettingsSubPageBrowserTest.call(this, 'basic');
+
+  /** @override */
+  this.subPages = [
     'people',
-    'internet',
     'appearance',
     'onStartup',
     'search',
-    'defaultBrowser',
-    'device'
   ];
-
-  SettingsSubPageBrowserTest.call(this, 'basic', subPages);
+  if (cr.isChromeOS)
+    this.subPages.push('device', 'internet');
+  else
+    this.subPages.push('defaultBrowser');
 }
 
 SettingsBasicSubPageBrowserTest.prototype = {
@@ -131,22 +114,33 @@
 
 /** @constructor @extends {SettingsSubPageBrowserTest} */
 function SettingsAdvancedSubPageBrowserTest() {
-  var subPages = [
-    'dateTime',
+  // "Advanced" sections live in the settings-basic-page.
+  SettingsSubPageBrowserTest.call(this, 'basic');
+
+  /** @override */
+  this.subPages = [
     'privacy',
-    'bluetooth',
     'passwordsAndForms',
     'languages',
     'downloads',
+    'printing',
+    'a11y',
     'reset',
-    'a11y'
   ];
-
-  SettingsSubPageBrowserTest.call(this, 'advanced', subPages);
+  if (cr.isChromeOS)
+    this.subPages.push('dateTime', 'bluetooth');
+  else
+    this.subPages.push('system');
 };
 
 SettingsAdvancedSubPageBrowserTest.prototype = {
   __proto__: SettingsSubPageBrowserTest.prototype,
+
+  /** @override */
+  setUp: function() {
+    this.toggleAdvanced();
+    SettingsSubPageBrowserTest.prototype.setUp.call(this);
+  },
 };
 
 TEST_F('SettingsAdvancedSubPageBrowserTest', 'SubPages', function() {
diff --git a/chromecast/browser/cast_content_browser_client.cc b/chromecast/browser/cast_content_browser_client.cc
index 7d9e4a3..071298b5 100644
--- a/chromecast/browser/cast_content_browser_client.cc
+++ b/chromecast/browser/cast_content_browser_client.cc
@@ -47,6 +47,7 @@
 #include "content/public/browser/client_certificate_delegate.h"
 #include "content/public/browser/render_process_host.h"
 #include "content/public/browser/resource_dispatcher_host.h"
+#include "content/public/browser/storage_partition.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/common/content_descriptors.h"
 #include "content/public/common/content_switches.h"
@@ -322,6 +323,16 @@
   return new CastQuotaPermissionContext();
 }
 
+void CastContentBrowserClient::GetQuotaSettings(
+    content::BrowserContext* context,
+    content::StoragePartition* partition,
+    const storage::OptionalQuotaSettingsCallback& callback) {
+  content::BrowserThread::PostTaskAndReplyWithResult(
+      content::BrowserThread::FILE, FROM_HERE,
+      base::Bind(&storage::CalculateNominalDynamicSettings,
+                 partition->GetPath(), context->IsOffTheRecord()),
+      callback);
+}
 void CastContentBrowserClient::AllowCertificateError(
     content::WebContents* web_contents,
     int cert_error,
diff --git a/chromecast/browser/cast_content_browser_client.h b/chromecast/browser/cast_content_browser_client.h
index 0ed31c1..cc07dcb 100644
--- a/chromecast/browser/cast_content_browser_client.h
+++ b/chromecast/browser/cast_content_browser_client.h
@@ -117,6 +117,10 @@
   void ResourceDispatcherHostCreated() override;
   std::string GetApplicationLocale() override;
   content::QuotaPermissionContext* CreateQuotaPermissionContext() override;
+  void GetQuotaSettings(
+      content::BrowserContext* context,
+      content::StoragePartition* partition,
+      const storage::OptionalQuotaSettingsCallback& callback) override;
   void AllowCertificateError(
       content::WebContents* web_contents,
       int cert_error,
diff --git a/content/browser/BUILD.gn b/content/browser/BUILD.gn
index e7b891e6..1872e6aa 100644
--- a/content/browser/BUILD.gn
+++ b/content/browser/BUILD.gn
@@ -190,6 +190,8 @@
     "$target_gen_dir/devtools/protocol/storage.h",
     "$target_gen_dir/devtools/protocol/system_info.cc",
     "$target_gen_dir/devtools/protocol/system_info.h",
+    "$target_gen_dir/devtools/protocol/target.cc",
+    "$target_gen_dir/devtools/protocol/target.h",
     "$target_gen_dir/devtools/protocol/tethering.cc",
     "$target_gen_dir/devtools/protocol/tethering.h",
     "$target_gen_dir/devtools/protocol/tracing.cc",
@@ -936,6 +938,8 @@
     "memory/memory_monitor_win.h",
     "memory/memory_pressure_controller_impl.cc",
     "memory/memory_pressure_controller_impl.h",
+    "memory/memory_state_updater.cc",
+    "memory/memory_state_updater.h",
     "message_port_message_filter.cc",
     "message_port_message_filter.h",
     "message_port_provider.cc",
diff --git a/content/browser/appcache/appcache_host_unittest.cc b/content/browser/appcache/appcache_host_unittest.cc
index 1e19f24..221afb21 100644
--- a/content/browser/appcache/appcache_host_unittest.cc
+++ b/content/browser/appcache/appcache_host_unittest.cc
@@ -110,7 +110,7 @@
     void GetUsageAndQuota(base::SequencedTaskRunner* original_task_runner,
                           const GURL& origin,
                           storage::StorageType type,
-                          const GetUsageAndQuotaCallback& callback) override {}
+                          const UsageAndQuotaCallback& callback) override {}
 
     void NotifyOriginInUse(const GURL& origin) override { inuse_[origin] += 1; }
 
diff --git a/content/browser/appcache/appcache_storage_impl_unittest.cc b/content/browser/appcache/appcache_storage_impl_unittest.cc
index b754546..d1ed641 100644
--- a/content/browser/appcache/appcache_storage_impl_unittest.cc
+++ b/content/browser/appcache/appcache_storage_impl_unittest.cc
@@ -277,12 +277,13 @@
                        base::FilePath(),
                        io_thread->task_runner().get(),
                        db_thread->task_runner().get(),
-                       NULL),
+                       nullptr,
+                       storage::GetQuotaSettingsFunc()),
           async_(false) {}
 
     void GetUsageAndQuota(const GURL& origin,
                           storage::StorageType type,
-                          const GetUsageAndQuotaCallback& callback) override {
+                          const UsageAndQuotaCallback& callback) override {
       EXPECT_EQ(storage::kStorageTypeTemporary, type);
       if (async_) {
         base::ThreadTaskRunnerHandle::Get()->PostTask(
@@ -293,7 +294,7 @@
       CallCallback(callback);
     }
 
-    void CallCallback(const GetUsageAndQuotaCallback& callback) {
+    void CallCallback(const UsageAndQuotaCallback& callback) {
       callback.Run(storage::kQuotaStatusOk, 0, kMockQuota);
     }
 
@@ -345,7 +346,7 @@
     void GetUsageAndQuota(base::SequencedTaskRunner* original_task_runner,
                           const GURL& origin,
                           storage::StorageType type,
-                          const GetUsageAndQuotaCallback& callback) override {}
+                          const UsageAndQuotaCallback& callback) override {}
 
     int notify_storage_accessed_count_;
     int notify_storage_modified_count_;
diff --git a/content/browser/background_sync/background_sync_manager_unittest.cc b/content/browser/background_sync/background_sync_manager_unittest.cc
index 3127778..7219d1f 100644
--- a/content/browser/background_sync/background_sync_manager_unittest.cc
+++ b/content/browser/background_sync/background_sync_manager_unittest.cc
@@ -136,9 +136,7 @@
     // Create a StoragePartition with the correct BrowserContext so that the
     // BackgroundSyncManager can find the BrowserContext through it.
     storage_partition_impl_.reset(new StoragePartitionImpl(
-        helper_->browser_context(), base::FilePath(), nullptr, nullptr, nullptr,
-        nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
-        nullptr, nullptr, nullptr));
+        helper_->browser_context(), base::FilePath(), nullptr));
     helper_->context_wrapper()->set_storage_partition(
         storage_partition_impl_.get());
 
diff --git a/content/browser/background_sync/background_sync_service_impl_unittest.cc b/content/browser/background_sync/background_sync_service_impl_unittest.cc
index 457d8ab..158c518 100644
--- a/content/browser/background_sync/background_sync_service_impl_unittest.cc
+++ b/content/browser/background_sync/background_sync_service_impl_unittest.cc
@@ -133,9 +133,7 @@
     // Creates a StoragePartition so that the BackgroundSyncManager can
     // use it to access the BrowserContext.
     storage_partition_impl_.reset(new StoragePartitionImpl(
-        embedded_worker_helper_->browser_context(), base::FilePath(), nullptr,
-        nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
-        nullptr, nullptr, nullptr, nullptr, nullptr));
+        embedded_worker_helper_->browser_context(), base::FilePath(), nullptr));
     embedded_worker_helper_->context_wrapper()->set_storage_partition(
         storage_partition_impl_.get());
   }
diff --git a/content/browser/browser_main_loop.cc b/content/browser/browser_main_loop.cc
index ff0c374..2d71994 100644
--- a/content/browser/browser_main_loop.cc
+++ b/content/browser/browser_main_loop.cc
@@ -93,7 +93,7 @@
 #include "device/time_zone_monitor/time_zone_monitor.h"
 #include "media/base/media.h"
 #include "media/base/user_input_monitor.h"
-#include "media/midi/midi_manager.h"
+#include "media/midi/midi_service.h"
 #include "mojo/edk/embedder/embedder.h"
 #include "mojo/edk/embedder/scoped_ipc_support.h"
 #include "net/base/network_change_notifier.h"
@@ -1140,9 +1140,9 @@
     resource_dispatcher_host_->Shutdown();
   }
   // Request shutdown to clean up allocated resources on the IO thread.
-  if (midi_manager_) {
-    TRACE_EVENT0("shutdown", "BrowserMainLoop::Subsystem:MidiManager");
-    midi_manager_->Shutdown();
+  if (midi_service_) {
+    TRACE_EVENT0("shutdown", "BrowserMainLoop::Subsystem:MidiService");
+    midi_service_->Shutdown();
   }
 
   memory_pressure_monitor_.reset();
@@ -1405,8 +1405,8 @@
   }
 
   {
-    TRACE_EVENT0("startup", "BrowserThreadsStarted::Subsystem:MidiManager");
-    midi_manager_.reset(midi::MidiManager::Create());
+    TRACE_EVENT0("startup", "BrowserThreadsStarted::Subsystem:MidiService");
+    midi_service_.reset(new midi::MidiService);
   }
 
 #if defined(OS_WIN)
diff --git a/content/browser/browser_main_loop.h b/content/browser/browser_main_loop.h
index 77c98c5..856a67b6 100644
--- a/content/browser/browser_main_loop.h
+++ b/content/browser/browser_main_loop.h
@@ -52,7 +52,7 @@
 }  // namespace media
 
 namespace midi {
-class MidiManager;
+class MidiService;
 }  // namespace midi
 
 namespace mojo {
@@ -145,7 +145,7 @@
   device::TimeZoneMonitor* time_zone_monitor() const {
     return time_zone_monitor_.get();
   }
-  midi::MidiManager* midi_manager() const { return midi_manager_.get(); }
+  midi::MidiService* midi_service() const { return midi_service_.get(); }
   base::Thread* indexed_db_thread() const { return indexed_db_thread_.get(); }
 
   bool is_tracing_startup_for_duration() const {
@@ -291,7 +291,7 @@
   std::unique_ptr<AudioManagerThread> audio_thread_;
   media::ScopedAudioManagerPtr audio_manager_;
 
-  std::unique_ptr<midi::MidiManager> midi_manager_;
+  std::unique_ptr<midi::MidiService> midi_service_;
 
 #if defined(OS_WIN)
   std::unique_ptr<media::SystemMessageWindowWin> system_message_window_;
diff --git a/content/browser/browsing_data/clear_site_data_throttle_browsertest.cc b/content/browser/browsing_data/clear_site_data_throttle_browsertest.cc
index f5e4364..f8ae1b6d 100644
--- a/content/browser/browsing_data/clear_site_data_throttle_browsertest.cc
+++ b/content/browser/browsing_data/clear_site_data_throttle_browsertest.cc
@@ -21,6 +21,7 @@
 #include "net/dns/mock_host_resolver.h"
 #include "net/test/embedded_test_server/http_request.h"
 #include "net/test/embedded_test_server/http_response.h"
+#include "storage/browser/quota/quota_settings.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "url/origin.h"
 #include "url/url_constants.h"
@@ -40,6 +41,13 @@
                     bool remove_storage,
                     bool remove_cache,
                     const base::Closure& callback));
+
+  void GetQuotaSettings(
+      content::BrowserContext* context,
+      content::StoragePartition* partition,
+      const storage::OptionalQuotaSettingsCallback& callback) override {
+    callback.Run(storage::GetHardCodedSettings(100 * 1024 * 1024));
+  }
 };
 
 class TestContentBrowserClient : public MockContentBrowserClient {
diff --git a/content/browser/database_tracker_unittest.cc b/content/browser/database_tracker_unittest.cc
index c1ec078..f3eea24 100644
--- a/content/browser/database_tracker_unittest.cc
+++ b/content/browser/database_tracker_unittest.cc
@@ -140,7 +140,7 @@
   void GetUsageAndQuota(base::SequencedTaskRunner* original_task_runner,
                         const GURL& origin,
                         storage::StorageType type,
-                        const GetUsageAndQuotaCallback& callback) override {}
+                        const UsageAndQuotaCallback& callback) override {}
 
   void SimulateQuotaManagerDestroyed() {
     if (registered_client_) {
diff --git a/content/browser/devtools/BUILD.gn b/content/browser/devtools/BUILD.gn
index ddda5baf..dc026ba 100644
--- a/content/browser/devtools/BUILD.gn
+++ b/content/browser/devtools/BUILD.gn
@@ -82,6 +82,8 @@
     "protocol/storage.h",
     "protocol/system_info.cc",
     "protocol/system_info.h",
+    "protocol/target.cc",
+    "protocol/target.h",
     "protocol/tethering.cc",
     "protocol/tethering.h",
     "protocol/tracing.cc",
diff --git a/content/browser/devtools/protocol/devtools_protocol_handler_generator.py b/content/browser/devtools/protocol/devtools_protocol_handler_generator.py
index 60178dd..34e0970b 100755
--- a/content/browser/devtools/protocol/devtools_protocol_handler_generator.py
+++ b/content/browser/devtools/protocol/devtools_protocol_handler_generator.py
@@ -643,7 +643,7 @@
 includes = []
 fields_init = []
 
-browser_domains_list = ["Target", "Input"]
+browser_domains_list = ["Input"]
 browser_commands_list = []
 async_commands_list = [
     "Input.synthesizePinchGesture",
diff --git a/content/browser/devtools/protocol/target_handler.cc b/content/browser/devtools/protocol/target_handler.cc
index 254a4ac..78778f4 100644
--- a/content/browser/devtools/protocol/target_handler.cc
+++ b/content/browser/devtools/protocol/target_handler.cc
@@ -11,10 +11,7 @@
 #include "content/browser/frame_host/render_frame_host_impl.h"
 
 namespace content {
-namespace devtools {
-namespace target {
-
-using Response = DevToolsProtocolClient::Response;
+namespace protocol {
 
 namespace {
 
@@ -84,12 +81,13 @@
   return result;
 }
 
-scoped_refptr<TargetInfo> CreateInfo(DevToolsAgentHost* host) {
-  return TargetInfo::Create()
-      ->set_target_id(host->GetId())
-      ->set_title(host->GetTitle())
-      ->set_url(host->GetURL().spec())
-      ->set_type(host->GetType());
+std::unique_ptr<Target::TargetInfo> CreateInfo(DevToolsAgentHost* host) {
+  return Target::TargetInfo::Create()
+      .SetTargetId(host->GetId())
+      .SetTitle(host->GetTitle())
+      .SetUrl(host->GetURL().spec())
+      .SetType(host->GetType())
+      .Build();
 }
 
 }  // namespace
@@ -103,7 +101,11 @@
 }
 
 TargetHandler::~TargetHandler() {
-  Detached();
+}
+
+void TargetHandler::Wire(UberDispatcher* dispatcher) {
+  frontend_.reset(new Target::Frontend(dispatcher->channel()));
+  Target::Dispatcher::wire(dispatcher, this);
 }
 
 void TargetHandler::SetRenderFrameHost(RenderFrameHostImpl* render_frame_host) {
@@ -111,16 +113,13 @@
   UpdateFrames();
 }
 
-void TargetHandler::SetClient(std::unique_ptr<Client> client) {
-  client_.swap(client);
-}
-
-void TargetHandler::Detached() {
+Response TargetHandler::Disable() {
   SetAutoAttach(false, false);
   SetDiscoverTargets(false);
   for (const auto& id_host : attached_hosts_)
     id_host.second->DetachClient(this);
   attached_hosts_.clear();
+  return Response::OK();
 }
 
 void TargetHandler::UpdateServiceWorkers() {
@@ -198,8 +197,7 @@
 void TargetHandler::TargetCreatedInternal(DevToolsAgentHost* host) {
   if (reported_hosts_.find(host->GetId()) != reported_hosts_.end())
     return;
-  client_->TargetCreated(
-      TargetCreatedParams::Create()->set_target_info(CreateInfo(host)));
+  frontend_->TargetCreated(CreateInfo(host));
   reported_hosts_[host->GetId()] = host;
 }
 
@@ -208,8 +206,7 @@
   auto it = reported_hosts_.find(host->GetId());
   if (it == reported_hosts_.end())
     return;
-  client_->TargetDestroyed(TargetDestroyedParams::Create()
-      ->set_target_id(host->GetId()));
+  frontend_->TargetDestroyed(host->GetId());
   reported_hosts_.erase(it);
 }
 
@@ -218,9 +215,7 @@
   if (!host->AttachClient(this))
     return false;
   attached_hosts_[host->GetId()] = host;
-  client_->AttachedToTarget(AttachedToTargetParams::Create()
-      ->set_target_info(CreateInfo(host))
-      ->set_waiting_for_debugger(waiting_for_debugger));
+  frontend_->AttachedToTarget(CreateInfo(host), waiting_for_debugger);
   return true;
 }
 
@@ -229,8 +224,7 @@
   if (it == attached_hosts_.end())
     return;
   host->DetachClient(this);
-  client_->DetachedFromTarget(DetachedFromTargetParams::Create()->
-      set_target_id(host->GetId()));
+  frontend_->DetachedFromTarget(host->GetId());
   attached_hosts_.erase(it);
 }
 
@@ -284,8 +278,8 @@
 }
 
 Response TargetHandler::SetRemoteLocations(
-    const std::vector<std::unique_ptr<base::DictionaryValue>>& locations) {
-  return Response::ServerError("Not supported");
+    std::unique_ptr<protocol::Array<Target::RemoteLocation>>) {
+  return Response::Error("Not supported");
 }
 
 Response TargetHandler::AttachToTarget(const std::string& target_id,
@@ -294,7 +288,7 @@
   scoped_refptr<DevToolsAgentHost> agent_host =
       DevToolsAgentHost::GetForId(target_id);
   if (!agent_host)
-    return Response::ServerError("No target with given id found");
+    return Response::InvalidParams("No target with given id found");
   *out_success = AttachToTargetInternal(agent_host.get(), false);
   return Response::OK();
 }
@@ -302,7 +296,7 @@
 Response TargetHandler::DetachFromTarget(const std::string& target_id) {
   auto it = attached_hosts_.find(target_id);
   if (it == attached_hosts_.end())
-    return Response::InternalError("Not attached to the target");
+    return Response::Error("Not attached to the target");
   DevToolsAgentHost* agent_host = it->second.get();
   DetachFromTargetInternal(agent_host);
   return Response::OK();
@@ -320,7 +314,7 @@
 
 Response TargetHandler::GetTargetInfo(
     const std::string& target_id,
-    scoped_refptr<TargetInfo>* target_info) {
+    std::unique_ptr<Target::TargetInfo>* target_info) {
   // TODO(dgozman): only allow reported hosts.
   scoped_refptr<DevToolsAgentHost> agent_host(
       DevToolsAgentHost::GetForId(target_id));
@@ -345,41 +339,42 @@
   scoped_refptr<DevToolsAgentHost> agent_host =
       DevToolsAgentHost::GetForId(target_id);
   if (!agent_host)
-    return Response::ServerError("No target with given id found");
+    return Response::InvalidParams("No target with given id found");
   *out_success = agent_host->Close();
   return Response::OK();
 }
 
 Response TargetHandler::CreateBrowserContext(std::string* out_context_id) {
-  return Response::ServerError("Not supported");
+  return Response::Error("Not supported");
 }
 
 Response TargetHandler::DisposeBrowserContext(const std::string& context_id,
                                               bool* out_success) {
-  return Response::ServerError("Not supported");
+  return Response::Error("Not supported");
 }
 
 Response TargetHandler::CreateTarget(const std::string& url,
-                                     const int* width,
-                                     const int* height,
-                                     const std::string* context_id,
+                                     Maybe<int> width,
+                                     Maybe<int> height,
+                                     Maybe<std::string> context_id,
                                      std::string* out_target_id) {
   DevToolsManagerDelegate* delegate =
       DevToolsManager::GetInstance()->delegate();
   if (!delegate)
-    return Response::ServerError("Not supported");
+    return Response::Error("Not supported");
   scoped_refptr<content::DevToolsAgentHost> agent_host =
       delegate->CreateNewTarget(GURL(url));
   if (!agent_host)
-    return Response::ServerError("Not supported");
+    return Response::Error("Not supported");
   *out_target_id = agent_host->GetId();
   return Response::OK();
 }
 
 Response TargetHandler::GetTargets(
-    std::vector<scoped_refptr<TargetInfo>>* target_infos) {
+    std::unique_ptr<protocol::Array<Target::TargetInfo>>* target_infos) {
+  *target_infos = protocol::Array<Target::TargetInfo>::create();
   for (const auto& host : DevToolsAgentHost::GetOrCreateAll())
-    target_infos->push_back(CreateInfo(host.get()));
+    (*target_infos)->addItem(CreateInfo(host.get()));
   return Response::OK();
 }
 
@@ -392,17 +387,13 @@
   if (it == attached_hosts_.end())
     return;  // Already disconnected.
 
-  client_->ReceivedMessageFromTarget(
-      ReceivedMessageFromTargetParams::Create()->
-          set_target_id(host->GetId())->
-          set_message(message));
+  frontend_->ReceivedMessageFromTarget(host->GetId(), message);
 }
 
 void TargetHandler::AgentHostClosed(
     DevToolsAgentHost* host,
     bool replaced_with_another_client) {
-  client_->DetachedFromTarget(DetachedFromTargetParams::Create()->
-      set_target_id(host->GetId()));
+  frontend_->DetachedFromTarget(host->GetId());
   attached_hosts_.erase(host->GetId());
 }
 
@@ -462,6 +453,5 @@
   UpdateServiceWorkers();
 }
 
-}  // namespace target
-}  // namespace devtools
+}  // namespace protocol
 }  // namespace content
diff --git a/content/browser/devtools/protocol/target_handler.h b/content/browser/devtools/protocol/target_handler.h
index c37c602..0b31029 100644
--- a/content/browser/devtools/protocol/target_handler.h
+++ b/content/browser/devtools/protocol/target_handler.h
@@ -8,7 +8,7 @@
 #include <map>
 #include <set>
 
-#include "content/browser/devtools/protocol/devtools_protocol_dispatcher.h"
+#include "content/browser/devtools/protocol/target.h"
 #include "content/browser/devtools/service_worker_devtools_manager.h"
 #include "content/public/browser/devtools_agent_host_client.h"
 #include "content/public/browser/devtools_agent_host_observer.h"
@@ -17,48 +17,52 @@
 
 class RenderFrameHostImpl;
 
-namespace devtools {
-namespace target {
+namespace protocol {
 
-class TargetHandler : public DevToolsAgentHostClient,
+class TargetHandler : public Target::Backend,
+                      public DevToolsAgentHostClient,
                       public ServiceWorkerDevToolsManager::Observer,
                       public DevToolsAgentHostObserver {
  public:
-  using Response = DevToolsProtocolClient::Response;
-
   TargetHandler();
   ~TargetHandler() override;
 
+  void Wire(UberDispatcher*);
   void SetRenderFrameHost(RenderFrameHostImpl* render_frame_host);
-  void SetClient(std::unique_ptr<Client> client);
-  void Detached();
+  Response Disable() override;
 
   void UpdateServiceWorkers();
   void UpdateFrames();
 
   // Domain implementation.
-  Response SetDiscoverTargets(bool discover);
-  Response SetAutoAttach(bool auto_attach, bool wait_for_debugger_on_start);
-  Response SetAttachToFrames(bool value);
+  Response SetDiscoverTargets(bool discover) override;
+  Response SetAutoAttach(bool auto_attach,
+                         bool wait_for_debugger_on_start) override;
+  Response SetAttachToFrames(bool value) override;
   Response SetRemoteLocations(
-      const std::vector<std::unique_ptr<base::DictionaryValue>>&);
-  Response AttachToTarget(const std::string& target_id, bool* out_success);
-  Response DetachFromTarget(const std::string& target_id);
+      std::unique_ptr<protocol::Array<Target::RemoteLocation>>) override;
+  Response AttachToTarget(const std::string& target_id,
+                          bool* out_success) override;
+  Response DetachFromTarget(const std::string& target_id) override;
   Response SendMessageToTarget(const std::string& target_id,
-                               const std::string& message);
-  Response GetTargetInfo(const std::string& target_id,
-                         scoped_refptr<TargetInfo>* target_info);
-  Response ActivateTarget(const std::string& target_id);
-  Response CloseTarget(const std::string& target_id, bool* out_success);
-  Response CreateBrowserContext(std::string* out_context_id);
+                               const std::string& message) override;
+  Response GetTargetInfo(
+      const std::string& target_id,
+      std::unique_ptr<Target::TargetInfo>* target_info) override;
+  Response ActivateTarget(const std::string& target_id) override;
+  Response CloseTarget(const std::string& target_id,
+                       bool* out_success) override;
+  Response CreateBrowserContext(std::string* out_context_id) override;
   Response DisposeBrowserContext(const std::string& context_id,
-                                 bool* out_success);
+                                 bool* out_success) override;
   Response CreateTarget(const std::string& url,
-                        const int* width,
-                        const int* height,
-                        const std::string* context_id,
-                        std::string* out_target_id);
-  Response GetTargets(std::vector<scoped_refptr<TargetInfo>>* target_infos);
+                        Maybe<int> width,
+                        Maybe<int> height,
+                        Maybe<std::string> context_id,
+                        std::string* out_target_id) override;
+  Response GetTargets(
+      std::unique_ptr<protocol::Array<Target::TargetInfo>>* target_infos)
+      override;
 
  private:
   using HostsMap = std::map<std::string, scoped_refptr<DevToolsAgentHost>>;
@@ -92,7 +96,7 @@
   void AgentHostClosed(DevToolsAgentHost* agent_host,
                        bool replaced_with_another_client) override;
 
-  std::unique_ptr<Client> client_;
+  std::unique_ptr<Target::Frontend> frontend_;
   bool discover_;
   bool auto_attach_;
   bool wait_for_debugger_on_start_;
@@ -105,8 +109,7 @@
   DISALLOW_COPY_AND_ASSIGN(TargetHandler);
 };
 
-}  // namespace target
-}  // namespace devtools
+}  // namespace protocol
 }  // namespace content
 
 #endif  // CONTENT_BROWSER_DEVTOOLS_PROTOCOL_TARGET_HANDLER_H_
diff --git a/content/browser/devtools/protocol_config.json b/content/browser/devtools/protocol_config.json
index d1f1d2c..3e4180b 100644
--- a/content/browser/devtools/protocol_config.json
+++ b/content/browser/devtools/protocol_config.json
@@ -62,6 +62,9 @@
                 "async": ["getInfo"]
             },
             {
+                "domain": "Target"
+            },
+            {
                 "domain": "Tethering",
                 "async": ["bind", "unbind"]
             },
diff --git a/content/browser/devtools/render_frame_devtools_agent_host.cc b/content/browser/devtools/render_frame_devtools_agent_host.cc
index ad707efe..76b3866 100644
--- a/content/browser/devtools/render_frame_devtools_agent_host.cc
+++ b/content/browser/devtools/render_frame_devtools_agent_host.cc
@@ -395,7 +395,6 @@
     RenderFrameHostImpl* host)
     : DevToolsAgentHostImpl(base::GenerateGUID()),
       input_handler_(new devtools::input::InputHandler()),
-      target_handler_(new devtools::target::TargetHandler()),
       frame_trace_recorder_(nullptr),
       protocol_handler_(new DevToolsProtocolHandler(this)),
       handlers_frame_host_(nullptr),
@@ -404,7 +403,6 @@
       frame_tree_node_(host->frame_tree_node()) {
   DevToolsProtocolDispatcher* dispatcher = protocol_handler_->dispatcher();
   dispatcher->SetInputHandler(input_handler_.get());
-  dispatcher->SetTargetHandler(target_handler_.get());
 
   SetPending(host);
   CommitPending();
@@ -514,6 +512,10 @@
   storage_handler_->Wire(session()->dispatcher());
   storage_handler_->SetRenderFrameHost(handlers_frame_host_);
 
+  target_handler_.reset(new protocol::TargetHandler());
+  target_handler_->Wire(session()->dispatcher());
+  target_handler_->SetRenderFrameHost(handlers_frame_host_);
+
   tracing_handler_.reset(new protocol::TracingHandler(
       protocol::TracingHandler::Renderer,
       frame_tree_node_->frame_tree_node_id(),
@@ -554,6 +556,8 @@
   service_worker_handler_.reset();
   storage_handler_->Disable();
   storage_handler_.reset();
+  target_handler_->Disable();
+  target_handler_.reset();
   tracing_handler_->Disable();
   tracing_handler_.reset();
 
@@ -618,7 +622,6 @@
 #if defined(OS_ANDROID)
   power_save_blocker_.reset();
 #endif
-  target_handler_->Detached();
   frame_trace_recorder_.reset();
   in_navigation_protocol_message_buffer_.clear();
 }
@@ -692,7 +695,7 @@
   DispatchBufferedProtocolMessagesIfNecessary();
 
   DCHECK(CheckConsistency());
-  if (navigation_handle->HasCommitted())
+  if (target_handler_ && navigation_handle->HasCommitted())
     target_handler_->UpdateServiceWorkers();
 }
 
@@ -738,7 +741,8 @@
   // CommitPending may destruct |this|.
   scoped_refptr<RenderFrameDevToolsAgentHost> protect(this);
 
-  target_handler_->UpdateFrames();
+  if (target_handler_)
+    target_handler_->UpdateFrames();
 
   if (IsBrowserSideNavigationEnabled())
     return;
@@ -905,7 +909,8 @@
   if (pending_ && pending_->host() == render_frame_host)
     CommitPending();
   DCHECK(CheckConsistency());
-  target_handler_->UpdateServiceWorkers();
+  if (target_handler_)
+    target_handler_->UpdateServiceWorkers();
 }
 
 void RenderFrameDevToolsAgentHost::DidFailProvisionalLoad(
@@ -972,7 +977,8 @@
     security_handler_->SetRenderFrameHost(host);
   if (storage_handler_)
     storage_handler_->SetRenderFrameHost(host);
-  target_handler_->SetRenderFrameHost(host);
+  if (target_handler_)
+    target_handler_->SetRenderFrameHost(host);
 }
 
 void RenderFrameDevToolsAgentHost::DisconnectWebContents() {
diff --git a/content/browser/devtools/render_frame_devtools_agent_host.h b/content/browser/devtools/render_frame_devtools_agent_host.h
index e4fa63e8..9a3bd65 100644
--- a/content/browser/devtools/render_frame_devtools_agent_host.h
+++ b/content/browser/devtools/render_frame_devtools_agent_host.h
@@ -41,7 +41,6 @@
 
 namespace devtools {
 namespace input { class InputHandler; }
-namespace target { class TargetHandler; }
 }
 
 namespace protocol {
@@ -55,6 +54,7 @@
 class SecurityHandler;
 class ServiceWorkerHandler;
 class StorageHandler;
+class TargetHandler;
 class TracingHandler;
 }  // namespace protocol
 
@@ -193,7 +193,7 @@
   std::unique_ptr<protocol::SecurityHandler> security_handler_;
   std::unique_ptr<protocol::ServiceWorkerHandler> service_worker_handler_;
   std::unique_ptr<protocol::StorageHandler> storage_handler_;
-  std::unique_ptr<devtools::target::TargetHandler> target_handler_;
+  std::unique_ptr<protocol::TargetHandler> target_handler_;
   std::unique_ptr<protocol::TracingHandler> tracing_handler_;
   std::unique_ptr<protocol::EmulationHandler> emulation_handler_;
   std::unique_ptr<DevToolsFrameTraceRecorder> frame_trace_recorder_;
diff --git a/content/browser/fileapi/file_system_browsertest.cc b/content/browser/fileapi/file_system_browsertest.cc
index 1bc55e74..ed7fa379 100644
--- a/content/browser/fileapi/file_system_browsertest.cc
+++ b/content/browser/fileapi/file_system_browsertest.cc
@@ -55,29 +55,27 @@
 class FileSystemBrowserTestWithLowQuota : public FileSystemBrowserTest {
  public:
   void SetUpOnMainThread() override {
-    const int kInitialQuotaKilobytes = 5000;
-    const int kTemporaryStorageQuotaMaxSize =
-        kInitialQuotaKilobytes * 1024 * QuotaManager::kPerHostTemporaryPortion;
-    SetTempQuota(
-        kTemporaryStorageQuotaMaxSize,
-        BrowserContext::GetDefaultStoragePartition(
-            shell()->web_contents()->GetBrowserContext())->GetQuotaManager());
+    SetLowQuota(BrowserContext::GetDefaultStoragePartition(
+                    shell()->web_contents()->GetBrowserContext())
+                    ->GetQuotaManager());
   }
 
-  static void SetTempQuota(int64_t bytes, scoped_refptr<QuotaManager> qm) {
+  static void SetLowQuota(scoped_refptr<QuotaManager> qm) {
     if (!BrowserThread::CurrentlyOn(BrowserThread::IO)) {
       BrowserThread::PostTask(
           BrowserThread::IO, FROM_HERE,
-          base::Bind(&FileSystemBrowserTestWithLowQuota::SetTempQuota, bytes,
-                     qm));
+          base::Bind(&FileSystemBrowserTestWithLowQuota::SetLowQuota, qm));
       return;
     }
     DCHECK_CURRENTLY_ON(BrowserThread::IO);
-    qm->SetTemporaryGlobalOverrideQuota(bytes, storage::QuotaCallback());
-    // Don't return until the quota has been set.
-    scoped_refptr<base::ThreadTestHelper> helper(new base::ThreadTestHelper(
-        BrowserThread::GetTaskRunnerForThread(BrowserThread::DB).get()));
-    ASSERT_TRUE(helper->Run());
+    // These sizes must correspond with expectations in html and js.
+    const int kMeg = 1000 * 1024;
+    storage::QuotaSettings settings;
+    settings.pool_size = 25 * kMeg;
+    settings.per_host_quota = 5 * kMeg;
+    settings.must_remain_available = 100 * kMeg;
+    settings.refresh_interval = base::TimeDelta::Max();
+    qm->SetQuotaSettings(settings);
   }
 };
 
diff --git a/content/browser/fileapi/obfuscated_file_util_unittest.cc b/content/browser/fileapi/obfuscated_file_util_unittest.cc
index 9eb3d5fa..8086c6bc 100644
--- a/content/browser/fileapi/obfuscated_file_util_unittest.cc
+++ b/content/browser/fileapi/obfuscated_file_util_unittest.cc
@@ -167,7 +167,14 @@
     quota_manager_ = new storage::QuotaManager(
         false /* is_incognito */, data_dir_.GetPath(),
         base::ThreadTaskRunnerHandle::Get().get(),
-        base::ThreadTaskRunnerHandle::Get().get(), storage_policy_.get());
+        base::ThreadTaskRunnerHandle::Get().get(), storage_policy_.get(),
+        storage::GetQuotaSettingsFunc());
+    storage::QuotaSettings settings;
+    settings.per_host_quota = 25 * 1024 * 1024;
+    settings.pool_size = settings.per_host_quota * 5;
+    settings.must_remain_available = 10 * 1024 * 1024;
+    settings.refresh_interval = base::TimeDelta::Max();
+    quota_manager_->SetQuotaSettings(settings);
 
     // Every time we create a new sandbox_file_system helper,
     // it creates another context, which creates another path manager,
diff --git a/content/browser/indexed_db/indexed_db_browsertest.cc b/content/browser/indexed_db/indexed_db_browsertest.cc
index 8c151a35..f4e7668c 100644
--- a/content/browser/indexed_db/indexed_db_browsertest.cc
+++ b/content/browser/indexed_db/indexed_db_browsertest.cc
@@ -122,27 +122,25 @@
     return static_cast<IndexedDBContextImpl*>(partition->GetIndexedDBContext());
   }
 
-  void SetQuota(int quota_kilobytes) {
-    const int kTemporaryStorageQuotaSize =
-        quota_kilobytes * 1024 * QuotaManager::kPerHostTemporaryPortion;
-    SetTempQuota(kTemporaryStorageQuotaSize,
-        BrowserContext::GetDefaultStoragePartition(
-            shell()->web_contents()->GetBrowserContext())->GetQuotaManager());
+  void SetQuota(int per_host_quota_kilobytes) {
+    SetTempQuota(per_host_quota_kilobytes,
+                 BrowserContext::GetDefaultStoragePartition(
+                     shell()->web_contents()->GetBrowserContext())
+                     ->GetQuotaManager());
   }
 
-  static void SetTempQuota(int64_t bytes, scoped_refptr<QuotaManager> qm) {
+  static void SetTempQuota(int per_host_quota_kilobytes,
+                           scoped_refptr<QuotaManager> qm) {
     if (!BrowserThread::CurrentlyOn(BrowserThread::IO)) {
-      BrowserThread::PostTask(
-          BrowserThread::IO, FROM_HERE,
-          base::Bind(&IndexedDBBrowserTest::SetTempQuota, bytes, qm));
+      BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
+                              base::Bind(&IndexedDBBrowserTest::SetTempQuota,
+                                         per_host_quota_kilobytes, qm));
       return;
     }
     DCHECK_CURRENTLY_ON(BrowserThread::IO);
-    qm->SetTemporaryGlobalOverrideQuota(bytes, storage::QuotaCallback());
-    // Don't return until the quota has been set.
-    scoped_refptr<base::ThreadTestHelper> helper(new base::ThreadTestHelper(
-        BrowserThread::GetTaskRunnerForThread(BrowserThread::DB)));
-    ASSERT_TRUE(helper->Run());
+    const int KB = 1024;
+    qm->SetQuotaSettings(
+        storage::GetHardCodedSettings(per_host_quota_kilobytes * KB));
   }
 
   virtual int64_t RequestDiskUsage() {
diff --git a/content/browser/loader/reload_cache_control_browsertest.cc b/content/browser/loader/reload_cache_control_browsertest.cc
index 93288ac..7d7a6818 100644
--- a/content/browser/loader/reload_cache_control_browsertest.cc
+++ b/content/browser/loader/reload_cache_control_browsertest.cc
@@ -8,10 +8,9 @@
 
 #include "base/bind.h"
 #include "base/bind_helpers.h"
-#include "base/command_line.h"
 #include "base/macros.h"
 #include "base/synchronization/lock.h"
-#include "content/public/common/content_switches.h"
+#include "content/public/common/browser_side_navigation_policy.h"
 #include "content/public/test/content_browser_test.h"
 #include "content/public/test/content_browser_test_utils.h"
 #include "content/shell/browser/shell.h"
@@ -189,8 +188,7 @@
 
   // TODO(crbug.com/671545): This test does not work correctly if browser-side
   // navigation is enabled.
-  if (base::CommandLine::ForCurrentProcess()->HasSwitch(
-          switches::kEnableBrowserSideNavigation))
+  if (IsBrowserSideNavigationEnabled())
     return;
 
   // The second navigation is the same page navigation. This should be handled
diff --git a/content/browser/media/midi_host.cc b/content/browser/media/midi_host.cc
index ed01c71..d85dc7d9a 100644
--- a/content/browser/media/midi_host.cc
+++ b/content/browser/media/midi_host.cc
@@ -15,8 +15,8 @@
 #include "content/public/browser/content_browser_client.h"
 #include "content/public/browser/user_metrics.h"
 #include "media/midi/message_util.h"
-#include "media/midi/midi_manager.h"
 #include "media/midi/midi_message_queue.h"
+#include "media/midi/midi_service.h"
 
 namespace content {
 namespace {
@@ -42,30 +42,29 @@
 using midi::mojom::PortState;
 using midi::mojom::Result;
 
-MidiHost::MidiHost(int renderer_process_id,
-                   midi::MidiManager* midi_manager)
+MidiHost::MidiHost(int renderer_process_id, midi::MidiService* midi_service)
     : BrowserMessageFilter(MidiMsgStart),
       renderer_process_id_(renderer_process_id),
       has_sys_ex_permission_(false),
       is_session_requested_(false),
-      midi_manager_(midi_manager),
+      midi_service_(midi_service),
       sent_bytes_in_flight_(0),
       bytes_sent_since_last_acknowledgement_(0),
       output_port_count_(0) {
-  DCHECK(midi_manager_);
+  DCHECK(midi_service_);
 }
 
 MidiHost::~MidiHost() = default;
 
 void MidiHost::OnChannelClosing() {
-  // If we get here the MidiHost is going to be destroyed soon. Prevent any
-  // subsequent calls from MidiManager by closing our session.
+  // If we get here the MidiHost is going to be destroyed soon.  Prevent any
+  // subsequent calls from MidiService by closing our session.
   // If Send() is called from a different thread (e.g. a separate thread owned
-  // by the MidiManager implementation), it will get posted to the IO thread.
+  // by the MidiService implementation), it will get posted to the IO thread.
   // There is a race condition here if our refcount is 0 and we're about to or
   // have already entered OnDestruct().
-  if (is_session_requested_ && midi_manager_) {
-    midi_manager_->EndSession(this);
+  if (is_session_requested_ && midi_service_) {
+    midi_service_->EndSession(this);
     is_session_requested_ = false;
   }
 }
@@ -89,8 +88,8 @@
 
 void MidiHost::OnStartSession() {
   is_session_requested_ = true;
-  if (midi_manager_)
-    midi_manager_->StartSession(this);
+  if (midi_service_)
+    midi_service_->StartSession(this);
 }
 
 void MidiHost::OnSendData(uint32_t port,
@@ -128,14 +127,14 @@
       return;
     sent_bytes_in_flight_ += data.size();
   }
-  if (midi_manager_)
-    midi_manager_->DispatchSendMidiData(this, port, data, timestamp);
+  if (midi_service_)
+    midi_service_->DispatchSendMidiData(this, port, data, timestamp);
 }
 
 void MidiHost::OnEndSession() {
   is_session_requested_ = false;
-  if (midi_manager_)
-    midi_manager_->EndSession(this);
+  if (midi_service_)
+    midi_service_->EndSession(this);
 }
 
 void MidiHost::CompleteStartSession(Result result) {
@@ -222,7 +221,7 @@
 }
 
 void MidiHost::Detach() {
-  midi_manager_ = nullptr;
+  midi_service_ = nullptr;
 }
 
 }  // namespace content
diff --git a/content/browser/media/midi_host.h b/content/browser/media/midi_host.h
index 95e69031f..bc6fd39 100644
--- a/content/browser/media/midi_host.h
+++ b/content/browser/media/midi_host.h
@@ -24,7 +24,7 @@
 #include "media/midi/midi_service.mojom.h"
 
 namespace midi {
-class MidiManager;
+class MidiService;
 class MidiMessageQueue;
 }  // namespace midi
 
@@ -34,7 +34,7 @@
                                 public midi::MidiManagerClient {
  public:
   // Called from UI thread from the owner of this object.
-  MidiHost(int renderer_process_id, midi::MidiManager* midi_manager);
+  MidiHost(int renderer_process_id, midi::MidiService* midi_service);
 
   // BrowserMessageFilter implementation.
   void OnChannelClosing() override;
@@ -80,12 +80,9 @@
   // Represents if a session is requested to start.
   bool is_session_requested_;
 
-  // |midi_manager_| talks to the platform-specific MIDI APIs.
-  // It can be NULL if the platform (or our current implementation)
-  // does not support MIDI.  If not supported then a call to
-  // OnRequestAccess() will always refuse access and a call to
-  // OnSendData() will do nothing.
-  midi::MidiManager* midi_manager_;
+  // |midi_service_| manages a MidiManager instance that talks to
+  // platform-specific MIDI APIs.  It can be nullptr after detached.
+  midi::MidiService* midi_service_;
 
   // Buffers where data sent from each MIDI input port is stored.
   ScopedVector<midi::MidiMessageQueue> received_messages_queues_;
diff --git a/content/browser/media/midi_host_unittest.cc b/content/browser/media/midi_host_unittest.cc
index 866d4469..8fea9a56 100644
--- a/content/browser/media/midi_host_unittest.cc
+++ b/content/browser/media/midi_host_unittest.cc
@@ -8,12 +8,14 @@
 #include <stdint.h>
 
 #include "base/macros.h"
+#include "base/memory/ptr_util.h"
 #include "base/message_loop/message_loop.h"
 #include "base/run_loop.h"
 #include "base/strings/stringprintf.h"
 #include "content/common/media/midi_messages.h"
 #include "content/public/test/test_browser_thread.h"
 #include "media/midi/midi_manager.h"
+#include "media/midi/midi_service.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace content {
@@ -60,9 +62,8 @@
 
 class MidiHostForTesting : public MidiHost {
  public:
-  MidiHostForTesting(int renderer_process_id,
-                     midi::MidiManager* midi_manager)
-      : MidiHost(renderer_process_id, midi_manager) {}
+  MidiHostForTesting(int renderer_process_id, midi::MidiService* midi_service)
+      : MidiHost(renderer_process_id, midi_service) {}
 
  private:
   ~MidiHostForTesting() override {}
@@ -78,11 +79,14 @@
  public:
   MidiHostTest()
       : io_browser_thread_(BrowserThread::IO, &message_loop_),
-        host_(new MidiHostForTesting(kRenderProcessId, &manager_)),
         data_(kNoteOn, kNoteOn + arraysize(kNoteOn)),
-        port_id_(0) {}
+        port_id_(0) {
+    manager_ = new FakeMidiManager;
+    service_.reset(new midi::MidiService(base::WrapUnique(manager_)));
+    host_ = new MidiHostForTesting(kRenderProcessId, service_.get());
+  }
   ~MidiHostTest() override {
-    manager_.Shutdown();
+    service_->Shutdown();
     RunLoopUntilIdle();
   }
 
@@ -104,15 +108,13 @@
     host_->OnMessageReceived(*message.get());
   }
 
-  size_t GetEventSize() const {
-    return manager_.events_.size();
-  }
+  size_t GetEventSize() const { return manager_->events_.size(); }
 
   void CheckSendEventAt(size_t at, uint32_t port) {
-    EXPECT_EQ(DISPATCH_SEND_MIDI_DATA, manager_.events_[at].type);
-    EXPECT_EQ(port, manager_.events_[at].port_index);
-    EXPECT_EQ(data_, manager_.events_[at].data);
-    EXPECT_EQ(0.0, manager_.events_[at].timestamp);
+    EXPECT_EQ(DISPATCH_SEND_MIDI_DATA, manager_->events_[at].type);
+    EXPECT_EQ(port, manager_->events_[at].port_index);
+    EXPECT_EQ(data_, manager_->events_[at].data);
+    EXPECT_EQ(0.0, manager_->events_[at].timestamp);
   }
 
   void RunLoopUntilIdle() {
@@ -124,10 +126,11 @@
   base::MessageLoop message_loop_;
   TestBrowserThread io_browser_thread_;
 
-  FakeMidiManager manager_;
-  scoped_refptr<MidiHostForTesting> host_;
   std::vector<uint8_t> data_;
   int32_t port_id_;
+  FakeMidiManager* manager_;  // Raw pointer for testing, owned by |service_|.
+  std::unique_ptr<midi::MidiService> service_;
+  scoped_refptr<MidiHostForTesting> host_;
 
   DISALLOW_COPY_AND_ASSIGN(MidiHostTest);
 };
diff --git a/content/browser/memory/memory_coordinator_impl.cc b/content/browser/memory/memory_coordinator_impl.cc
index ca18cba..5a24676 100644
--- a/content/browser/memory/memory_coordinator_impl.cc
+++ b/content/browser/memory/memory_coordinator_impl.cc
@@ -9,8 +9,8 @@
 #include "base/strings/string_number_conversions.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "base/trace_event/trace_event.h"
-#include "components/variations/variations_associated_data.h"
 #include "content/browser/memory/memory_monitor.h"
+#include "content/browser/memory/memory_state_updater.h"
 #include "content/public/browser/notification_service.h"
 #include "content/public/browser/notification_types.h"
 #include "content/public/browser/render_process_host.h"
@@ -21,24 +21,6 @@
 
 namespace {
 
-// A expected renderer size. These values come from the median of appropriate
-// UMA stats.
-#if defined(OS_ANDROID) || defined(OS_IOS)
-const int kDefaultExpectedRendererSizeMB = 40;
-#elif defined(OS_WIN)
-const int kDefaultExpectedRendererSizeMB = 70;
-#else // Mac, Linux, and ChromeOS
-const int kDefaultExpectedRendererSizeMB = 120;
-#endif
-
-// Default values for parameters to determine the global state.
-const int kDefaultNewRenderersUntilThrottled = 4;
-const int kDefaultNewRenderersUntilSuspended = 2;
-const int kDefaultNewRenderersBackToNormal = 5;
-const int kDefaultNewRenderersBackToThrottled = 3;
-const int kDefaultMinimumTransitionPeriodSeconds = 30;
-const int kDefaultMonitoringIntervalSeconds = 5;
-
 mojom::MemoryState ToMojomMemoryState(base::MemoryState state) {
   switch (state) {
     case base::MemoryState::UNKNOWN:
@@ -111,33 +93,6 @@
 #undef RECORD_METRICS
 }
 
-void SetIntVariationParameter(const std::map<std::string, std::string> params,
-                              const char* name,
-                              int* target) {
-  const auto& iter = params.find(name);
-  if (iter == params.end())
-    return;
-  int value;
-  if (!iter->second.empty() && base::StringToInt(iter->second, &value)) {
-    DCHECK(value > 0);
-    *target = value;
-  }
-}
-
-void SetSecondsVariationParameter(
-    const std::map<std::string, std::string> params,
-    const char* name,
-    base::TimeDelta* target) {
-  const auto& iter = params.find(name);
-  if (iter == params.end())
-    return;
-  int value;
-  if (!iter->second.empty() && base::StringToInt(iter->second, &value)) {
-    DCHECK(value > 0);
-    *target = base::TimeDelta::FromSeconds(value);
-  }
-}
-
 }  // namespace
 
 // SingletonTraits for MemoryCoordinator. Returns MemoryCoordinatorImpl
@@ -161,11 +116,9 @@
 MemoryCoordinatorImpl::MemoryCoordinatorImpl(
     scoped_refptr<base::SingleThreadTaskRunner> task_runner,
     std::unique_ptr<MemoryMonitor> memory_monitor)
-    : task_runner_(task_runner),
-      memory_monitor_(std::move(memory_monitor)),
-      weak_ptr_factory_(this) {
+    : memory_monitor_(std::move(memory_monitor)),
+      state_updater_(base::MakeUnique<MemoryStateUpdater>(this, task_runner)) {
   DCHECK(memory_monitor_.get());
-  InitializeParameters();
 }
 
 MemoryCoordinatorImpl::~MemoryCoordinatorImpl() {}
@@ -173,13 +126,12 @@
 void MemoryCoordinatorImpl::Start() {
   DCHECK(CalledOnValidThread());
   DCHECK(last_state_change_.is_null());
-  DCHECK(ValidateParameters());
 
   notification_registrar_.Add(
       this, NOTIFICATION_RENDER_WIDGET_VISIBILITY_CHANGED,
       NotificationService::AllBrowserContextsAndSources());
   last_state_change_ = base::TimeTicks::Now();
-  ScheduleUpdateState(base::TimeDelta());
+  state_updater_->ScheduleUpdateState(base::TimeDelta());
 }
 
 void MemoryCoordinatorImpl::OnChildAdded(int render_process_id) {
@@ -211,7 +163,7 @@
                                                 base::TimeDelta duration) {
   DCHECK(new_state != MemoryState::UNKNOWN);
   ChangeStateIfNeeded(current_state_, new_state);
-  ScheduleUpdateState(duration);
+  state_updater_->ScheduleUpdateState(duration);
 }
 
 void MemoryCoordinatorImpl::Observe(int type,
@@ -232,6 +184,7 @@
 
 bool MemoryCoordinatorImpl::ChangeStateIfNeeded(base::MemoryState prev_state,
                                                 base::MemoryState next_state) {
+  DCHECK(CalledOnValidThread());
   if (prev_state == next_state)
     return false;
 
@@ -249,57 +202,6 @@
   return true;
 }
 
-base::MemoryState MemoryCoordinatorImpl::CalculateNextState() {
-  using MemoryState = base::MemoryState;
-
-  int available = memory_monitor_->GetFreeMemoryUntilCriticalMB();
-
-  // TODO(chrisha): Move this histogram recording to a better place when
-  // https://codereview.chromium.org/2479673002/ is landed.
-  UMA_HISTOGRAM_MEMORY_LARGE_MB("Memory.Coordinator.FreeMemoryUntilCritical",
-                                available);
-
-  if (available <= 0)
-    return MemoryState::SUSPENDED;
-
-  int expected_renderer_count = available / expected_renderer_size_;
-
-  switch (current_state_) {
-    case MemoryState::NORMAL:
-      if (expected_renderer_count <= new_renderers_until_suspended_)
-        return MemoryState::SUSPENDED;
-      if (expected_renderer_count <= new_renderers_until_throttled_)
-        return MemoryState::THROTTLED;
-      return MemoryState::NORMAL;
-    case MemoryState::THROTTLED:
-      if (expected_renderer_count <= new_renderers_until_suspended_)
-        return MemoryState::SUSPENDED;
-      if (expected_renderer_count >= new_renderers_back_to_normal_)
-        return MemoryState::NORMAL;
-      return MemoryState::THROTTLED;
-    case MemoryState::SUSPENDED:
-      if (expected_renderer_count >= new_renderers_back_to_normal_)
-        return MemoryState::NORMAL;
-      if (expected_renderer_count >= new_renderers_back_to_throttled_)
-        return MemoryState::THROTTLED;
-      return MemoryState::SUSPENDED;
-    case MemoryState::UNKNOWN:
-      // Fall through
-    default:
-      NOTREACHED();
-      return MemoryState::UNKNOWN;
-  }
-}
-
-void MemoryCoordinatorImpl::UpdateState() {
-  MemoryState next_state = CalculateNextState();
-  if (ChangeStateIfNeeded(current_state_, next_state)) {
-    ScheduleUpdateState(minimum_transition_period_);
-  } else {
-    ScheduleUpdateState(monitoring_interval_);
-  }
-}
-
 void MemoryCoordinatorImpl::NotifyStateToClients() {
   auto state = GetCurrentMemoryState();
   base::MemoryCoordinatorClientRegistry::GetInstance()->Notify(state);
@@ -342,49 +244,4 @@
                              total_private_kb / 1024);
 }
 
-void MemoryCoordinatorImpl::ScheduleUpdateState(base::TimeDelta delta) {
-  update_state_closure_.Reset(base::Bind(&MemoryCoordinatorImpl::UpdateState,
-                                         weak_ptr_factory_.GetWeakPtr()));
-  task_runner_->PostDelayedTask(FROM_HERE, update_state_closure_.callback(),
-                                delta);
-}
-
-void MemoryCoordinatorImpl::InitializeParameters() {
-  expected_renderer_size_ = kDefaultExpectedRendererSizeMB;
-  new_renderers_until_throttled_ = kDefaultNewRenderersUntilThrottled;
-  new_renderers_until_suspended_ = kDefaultNewRenderersUntilSuspended;
-  new_renderers_back_to_normal_ = kDefaultNewRenderersBackToNormal;
-  new_renderers_back_to_throttled_ = kDefaultNewRenderersBackToThrottled;
-  minimum_transition_period_ =
-      base::TimeDelta::FromSeconds(kDefaultMinimumTransitionPeriodSeconds);
-  monitoring_interval_ =
-      base::TimeDelta::FromSeconds(kDefaultMonitoringIntervalSeconds);
-
-  // Override default parameters with variations.
-  static constexpr char kMemoryCoordinatorV0Trial[] = "MemoryCoordinatorV0";
-  std::map<std::string, std::string> params;
-  variations::GetVariationParams(kMemoryCoordinatorV0Trial, &params);
-  SetIntVariationParameter(params, "expected_renderer_size",
-                           &expected_renderer_size_);
-  SetIntVariationParameter(params, "new_renderers_until_throttled",
-                           &new_renderers_until_throttled_);
-  SetIntVariationParameter(params, "new_renderers_until_suspended",
-                           &new_renderers_until_suspended_);
-  SetIntVariationParameter(params, "new_renderers_back_to_normal",
-                           &new_renderers_back_to_normal_);
-  SetIntVariationParameter(params, "new_renderers_back_to_throttled",
-                           &new_renderers_back_to_throttled_);
-  SetSecondsVariationParameter(params, "minimum_transition_period",
-                               &minimum_transition_period_);
-  SetSecondsVariationParameter(params, "monitoring_interval",
-                               &monitoring_interval_);
-}
-
-bool MemoryCoordinatorImpl::ValidateParameters() {
-  return (new_renderers_until_throttled_ > new_renderers_until_suspended_) &&
-      (new_renderers_back_to_normal_ > new_renderers_back_to_throttled_) &&
-      (new_renderers_back_to_normal_ > new_renderers_until_throttled_) &&
-      (new_renderers_back_to_throttled_ > new_renderers_until_suspended_);
-}
-
 }  // namespace content
diff --git a/content/browser/memory/memory_coordinator_impl.h b/content/browser/memory/memory_coordinator_impl.h
index 95c2a2c..014bb528 100644
--- a/content/browser/memory/memory_coordinator_impl.h
+++ b/content/browser/memory/memory_coordinator_impl.h
@@ -7,7 +7,6 @@
 
 #include "base/callback.h"
 #include "base/memory/singleton.h"
-#include "base/memory/weak_ptr.h"
 #include "base/single_thread_task_runner.h"
 #include "base/threading/non_thread_safe.h"
 #include "base/time/time.h"
@@ -19,40 +18,32 @@
 
 class MemoryMonitor;
 class MemoryCoordinatorImplTest;
+class MemoryStateUpdater;
 struct MemoryCoordinatorSingletonTraits;
 
-// MemoryCoordinatorImpl is an internal implementation of MemoryCoordinator
-// which uses a heuristic to determine a single global memory state.
-// In the current implementation browser process and renderer processes share
-// the global state; the memory coordinator will notify the global state to
-// all background renderers if the state has changed.
-//
-// State calculation:
-// MemoryCoordinatorImpl uses followings to determine the global state:
-// * Compute "number of renderers which can be created until the system will
-//   be in a critical state". Call this N.
-//   (See memory_monitor.h for the definition of "critical")
-// * Covert N to a memory state using some thresholds/hysteresis for each state.
-//   Once a state is changed to a limited state, larger N will be needed to go
-//   back to a relaxed state. (e.g. THROTTLED -> NORMAL)
-// * Once a state is changed, it remains the same for a certain period of time.
+// MemoryCoordinatorImpl is an implementation of MemoryCoordinator.
+// The current implementation uses MemoryStateUpdater to update the global
+// memory state. See comments in MemoryStateUpdater for details.
 class CONTENT_EXPORT MemoryCoordinatorImpl : public MemoryCoordinator,
                                              public NotificationObserver,
                                              public base::NonThreadSafe {
  public:
+  using MemoryState = base::MemoryState;
+
   MemoryCoordinatorImpl(scoped_refptr<base::SingleThreadTaskRunner> task_runner,
                         std::unique_ptr<MemoryMonitor> monitor);
   ~MemoryCoordinatorImpl() override;
 
+  MemoryMonitor* memory_monitor() { return memory_monitor_.get(); }
+  MemoryStateUpdater* state_updater() { return state_updater_.get(); }
+
   // MemoryCoordinator implementations:
   void Start() override;
   void OnChildAdded(int render_process_id) override;
 
-  MemoryMonitor* memory_monitor() { return memory_monitor_.get(); }
-
-  base::MemoryState GetGlobalMemoryState() const override;
-  base::MemoryState GetCurrentMemoryState() const override;
-  void SetCurrentMemoryStateForTesting(base::MemoryState memory_state) override;
+  MemoryState GetGlobalMemoryState() const override;
+  MemoryState GetCurrentMemoryState() const override;
+  void SetCurrentMemoryStateForTesting(MemoryState memory_state) override;
 
   // NotificationObserver implementation:
   void Observe(int type,
@@ -65,6 +56,11 @@
   void ForceSetGlobalState(base::MemoryState new_state,
                            base::TimeDelta duration);
 
+  // Changes the global state and notifies state changes to clients (lives in
+  // the browser) and child processes (renderers) if needed. Returns true when
+  // the state is actually changed.
+  bool ChangeStateIfNeeded(MemoryState prev_state, MemoryState next_state);
+
  private:
   FRIEND_TEST_ALL_PREFIXES(MemoryCoordinatorImplTest, CalculateNextState);
   FRIEND_TEST_ALL_PREFIXES(MemoryCoordinatorImplTest, UpdateState);
@@ -73,20 +69,6 @@
 
   friend struct MemoryCoordinatorSingletonTraits;
 
-  using MemoryState = base::MemoryState;
-
-  // Changes the global state and notifies state changes to clients (lives in
-  // the browser) and child processes (renderers) if needed. Returns true when
-  // the state is actually changed.
-  bool ChangeStateIfNeeded(MemoryState prev_state, MemoryState next_state);
-
-  // Calculates the next global state from the amount of free memory using
-  // a heuristic.
-  MemoryState CalculateNextState();
-
-  // Periodically called to update the global state.
-  void UpdateState();
-
   // Notifies a state change to in-process clients.
   void NotifyStateToClients();
 
@@ -98,52 +80,12 @@
                          MemoryState next_state,
                          base::TimeDelta duration);
 
-  // Schedules a task to update the global state. The task will be executed
-  // after |delay| has passed.
-  void ScheduleUpdateState(base::TimeDelta delay);
-
-  scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
-  NotificationRegistrar notification_registrar_;
   std::unique_ptr<MemoryMonitor> memory_monitor_;
-  base::CancelableClosure update_state_closure_;
-  base::MemoryState current_state_ = MemoryState::NORMAL;
+  NotificationRegistrar notification_registrar_;
+  std::unique_ptr<MemoryStateUpdater> state_updater_;
+  MemoryState current_state_ = MemoryState::NORMAL;
   base::TimeTicks last_state_change_;
 
-  // Sets up parameters for the heuristic.
-  void InitializeParameters();
-
-  // Validates parameters defined below.
-  bool ValidateParameters();
-
-  // Parameters to control the heuristic.
-
-  // The median size of a renderer on the current platform. This is used to
-  // convert the amount of free memory to an expected number of new renderers
-  // that could be started before hitting critical memory pressure.
-  int expected_renderer_size_;
-  // When in a NORMAL state and the potential number of new renderers drops
-  // below this level, the coordinator will transition to a THROTTLED state.
-  int new_renderers_until_throttled_;
-  // When in a NORMAL/THROTTLED state and the potential number of new renderers
-  // drops below this level, the coordinator will transition to a SUSPENDED
-  // state.
-  int new_renderers_until_suspended_;
-  // When in a THROTTLED/SUSPENDED state and the potential number of new
-  // renderers rises above this level, the coordinator will transition to a
-  // NORMAL state.
-  int new_renderers_back_to_normal_;
-  // When in a SUSPENDED state and the potential number of new renderers rises
-  // above this level, the coordinator will transition to a SUSPENDED state.
-  int new_renderers_back_to_throttled_;
-  // The memory coordinator stays in the same state at least this duration even
-  // when there are considerable changes in the amount of free memory to prevent
-  // thrashing.
-  base::TimeDelta minimum_transition_period_;
-  // The interval of checking the amount of free memory.
-  base::TimeDelta monitoring_interval_;
-
-  base::WeakPtrFactory<MemoryCoordinatorImpl> weak_ptr_factory_;
-
   DISALLOW_COPY_AND_ASSIGN(MemoryCoordinatorImpl);
 };
 
diff --git a/content/browser/memory/memory_coordinator_impl_unittest.cc b/content/browser/memory/memory_coordinator_impl_unittest.cc
index 785de04..b2d8530 100644
--- a/content/browser/memory/memory_coordinator_impl_unittest.cc
+++ b/content/browser/memory/memory_coordinator_impl_unittest.cc
@@ -9,6 +9,7 @@
 #include "base/test/scoped_feature_list.h"
 #include "base/test/test_mock_time_task_runner.h"
 #include "content/browser/memory/memory_monitor.h"
+#include "content/browser/memory/memory_state_updater.h"
 #include "content/public/common/content_features.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -82,12 +83,13 @@
 };
 
 TEST_F(MemoryCoordinatorImplTest, CalculateNextState) {
-  coordinator_->expected_renderer_size_ = 10;
-  coordinator_->new_renderers_until_throttled_ = 4;
-  coordinator_->new_renderers_until_suspended_ = 2;
-  coordinator_->new_renderers_back_to_normal_ = 5;
-  coordinator_->new_renderers_back_to_throttled_ = 3;
-  DCHECK(coordinator_->ValidateParameters());
+  auto* state_updater = coordinator_->state_updater_.get();
+  state_updater->expected_renderer_size_ = 10;
+  state_updater->new_renderers_until_throttled_ = 4;
+  state_updater->new_renderers_until_suspended_ = 2;
+  state_updater->new_renderers_back_to_normal_ = 5;
+  state_updater->new_renderers_back_to_throttled_ = 3;
+  DCHECK(state_updater->ValidateParameters());
 
   // The default state is NORMAL.
   EXPECT_EQ(base::MemoryState::NORMAL, coordinator_->GetCurrentMemoryState());
@@ -103,11 +105,11 @@
                 GetCurrentMemoryState());
 
   GetMockMemoryMonitor()->SetFreeMemoryUntilCriticalMB(50);
-  EXPECT_EQ(base::MemoryState::NORMAL, coordinator_->CalculateNextState());
+  EXPECT_EQ(base::MemoryState::NORMAL, state_updater->CalculateNextState());
   GetMockMemoryMonitor()->SetFreeMemoryUntilCriticalMB(40);
-  EXPECT_EQ(base::MemoryState::THROTTLED, coordinator_->CalculateNextState());
+  EXPECT_EQ(base::MemoryState::THROTTLED, state_updater->CalculateNextState());
   GetMockMemoryMonitor()->SetFreeMemoryUntilCriticalMB(20);
-  EXPECT_EQ(base::MemoryState::SUSPENDED, coordinator_->CalculateNextState());
+  EXPECT_EQ(base::MemoryState::SUSPENDED, state_updater->CalculateNextState());
 
   // Transitions from THROTTLED
   coordinator_->current_state_ = base::MemoryState::THROTTLED;
@@ -118,11 +120,11 @@
                 GetCurrentMemoryState());
 
   GetMockMemoryMonitor()->SetFreeMemoryUntilCriticalMB(40);
-  EXPECT_EQ(base::MemoryState::THROTTLED, coordinator_->CalculateNextState());
+  EXPECT_EQ(base::MemoryState::THROTTLED, state_updater->CalculateNextState());
   GetMockMemoryMonitor()->SetFreeMemoryUntilCriticalMB(50);
-  EXPECT_EQ(base::MemoryState::NORMAL, coordinator_->CalculateNextState());
+  EXPECT_EQ(base::MemoryState::NORMAL, state_updater->CalculateNextState());
   GetMockMemoryMonitor()->SetFreeMemoryUntilCriticalMB(20);
-  EXPECT_EQ(base::MemoryState::SUSPENDED, coordinator_->CalculateNextState());
+  EXPECT_EQ(base::MemoryState::SUSPENDED, state_updater->CalculateNextState());
 
   // Transitions from SUSPENDED
   coordinator_->current_state_ = base::MemoryState::SUSPENDED;
@@ -135,20 +137,21 @@
                 GetCurrentMemoryState());
 
   GetMockMemoryMonitor()->SetFreeMemoryUntilCriticalMB(20);
-  EXPECT_EQ(base::MemoryState::SUSPENDED, coordinator_->CalculateNextState());
+  EXPECT_EQ(base::MemoryState::SUSPENDED, state_updater->CalculateNextState());
   GetMockMemoryMonitor()->SetFreeMemoryUntilCriticalMB(30);
-  EXPECT_EQ(base::MemoryState::THROTTLED, coordinator_->CalculateNextState());
+  EXPECT_EQ(base::MemoryState::THROTTLED, state_updater->CalculateNextState());
   GetMockMemoryMonitor()->SetFreeMemoryUntilCriticalMB(50);
-  EXPECT_EQ(base::MemoryState::NORMAL, coordinator_->CalculateNextState());
+  EXPECT_EQ(base::MemoryState::NORMAL, state_updater->CalculateNextState());
 }
 
 TEST_F(MemoryCoordinatorImplTest, UpdateState) {
-  coordinator_->expected_renderer_size_ = 10;
-  coordinator_->new_renderers_until_throttled_ = 4;
-  coordinator_->new_renderers_until_suspended_ = 2;
-  coordinator_->new_renderers_back_to_normal_ = 5;
-  coordinator_->new_renderers_back_to_throttled_ = 3;
-  DCHECK(coordinator_->ValidateParameters());
+  auto* state_updater = coordinator_->state_updater_.get();
+  state_updater->expected_renderer_size_ = 10;
+  state_updater->new_renderers_until_throttled_ = 4;
+  state_updater->new_renderers_until_suspended_ = 2;
+  state_updater->new_renderers_back_to_normal_ = 5;
+  state_updater->new_renderers_back_to_throttled_ = 3;
+  DCHECK(state_updater->ValidateParameters());
 
   {
     // Transition happens (NORMAL -> THROTTLED).
@@ -156,7 +159,7 @@
     base::MemoryCoordinatorClientRegistry::GetInstance()->Register(&client);
     coordinator_->current_state_ = base::MemoryState::NORMAL;
     GetMockMemoryMonitor()->SetFreeMemoryUntilCriticalMB(40);
-    coordinator_->UpdateState();
+    state_updater->UpdateState();
     base::RunLoop loop;
     loop.RunUntilIdle();
     EXPECT_TRUE(client.is_called());
@@ -170,7 +173,7 @@
     base::MemoryCoordinatorClientRegistry::GetInstance()->Register(&client);
     coordinator_->current_state_ = base::MemoryState::NORMAL;
     GetMockMemoryMonitor()->SetFreeMemoryUntilCriticalMB(50);
-    coordinator_->UpdateState();
+    state_updater->UpdateState();
     base::RunLoop loop;
     loop.RunUntilIdle();
     EXPECT_FALSE(client.is_called());
@@ -180,12 +183,13 @@
 }
 
 TEST_F(MemoryCoordinatorImplTest, SetMemoryStateForTesting) {
-  coordinator_->expected_renderer_size_ = 10;
-  coordinator_->new_renderers_until_throttled_ = 4;
-  coordinator_->new_renderers_until_suspended_ = 2;
-  coordinator_->new_renderers_back_to_normal_ = 5;
-  coordinator_->new_renderers_back_to_throttled_ = 3;
-  DCHECK(coordinator_->ValidateParameters());
+  auto* state_updater = coordinator_->state_updater_.get();
+  state_updater->expected_renderer_size_ = 10;
+  state_updater->new_renderers_until_throttled_ = 4;
+  state_updater->new_renderers_until_suspended_ = 2;
+  state_updater->new_renderers_back_to_normal_ = 5;
+  state_updater->new_renderers_back_to_throttled_ = 3;
+  DCHECK(state_updater->ValidateParameters());
 
   MockMemoryCoordinatorClient client;
   base::MemoryCoordinatorClientRegistry::GetInstance()->Register(&client);
@@ -220,18 +224,19 @@
 }
 
 TEST_F(MemoryCoordinatorImplTest, ForceSetGlobalState) {
-  coordinator_->expected_renderer_size_ = 10;
-  coordinator_->new_renderers_until_throttled_ = 4;
-  coordinator_->new_renderers_until_suspended_ = 2;
-  coordinator_->new_renderers_back_to_normal_ = 5;
-  coordinator_->new_renderers_back_to_throttled_ = 3;
-  DCHECK(coordinator_->ValidateParameters());
+  auto* state_updater = coordinator_->state_updater_.get();
+  state_updater->expected_renderer_size_ = 10;
+  state_updater->new_renderers_until_throttled_ = 4;
+  state_updater->new_renderers_until_suspended_ = 2;
+  state_updater->new_renderers_back_to_normal_ = 5;
+  state_updater->new_renderers_back_to_throttled_ = 3;
+  DCHECK(state_updater->ValidateParameters());
   GetMockMemoryMonitor()->SetFreeMemoryUntilCriticalMB(50);
 
   base::TimeDelta interval = base::TimeDelta::FromSeconds(5);
   base::TimeDelta minimum_transition = base::TimeDelta::FromSeconds(30);
-  coordinator_->monitoring_interval_ = interval;
-  coordinator_->minimum_transition_period_ = minimum_transition;
+  state_updater->monitoring_interval_ = interval;
+  state_updater->minimum_transition_period_ = minimum_transition;
 
   // Starts updating states. The initial state should be NORMAL with above
   // configuration.
diff --git a/content/browser/memory/memory_state_updater.cc b/content/browser/memory/memory_state_updater.cc
new file mode 100644
index 0000000..fb75dbd2
--- /dev/null
+++ b/content/browser/memory/memory_state_updater.cc
@@ -0,0 +1,173 @@
+// 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.
+
+#include "content/browser/memory/memory_state_updater.h"
+
+#include "base/metrics/histogram_macros.h"
+#include "base/strings/string_number_conversions.h"
+#include "components/variations/variations_associated_data.h"
+#include "content/browser/memory/memory_monitor.h"
+
+namespace content {
+
+namespace {
+
+// A expected renderer size. These values come from the median of appropriate
+// UMA stats.
+#if defined(OS_ANDROID) || defined(OS_IOS)
+const int kDefaultExpectedRendererSizeMB = 40;
+#elif defined(OS_WIN)
+const int kDefaultExpectedRendererSizeMB = 70;
+#else // Mac, Linux, and ChromeOS
+const int kDefaultExpectedRendererSizeMB = 120;
+#endif
+
+// Default values for parameters to determine the global state.
+const int kDefaultNewRenderersUntilThrottled = 4;
+const int kDefaultNewRenderersUntilSuspended = 2;
+const int kDefaultNewRenderersBackToNormal = 5;
+const int kDefaultNewRenderersBackToThrottled = 3;
+const int kDefaultMinimumTransitionPeriodSeconds = 30;
+const int kDefaultMonitoringIntervalSeconds = 5;
+
+void SetIntVariationParameter(const std::map<std::string, std::string> params,
+                              const char* name,
+                              int* target) {
+  const auto& iter = params.find(name);
+  if (iter == params.end())
+    return;
+  int value;
+  if (!iter->second.empty() && base::StringToInt(iter->second, &value)) {
+    DCHECK(value > 0);
+    *target = value;
+  }
+}
+
+void SetSecondsVariationParameter(
+    const std::map<std::string, std::string> params,
+    const char* name,
+    base::TimeDelta* target) {
+  const auto& iter = params.find(name);
+  if (iter == params.end())
+    return;
+  int value;
+  if (!iter->second.empty() && base::StringToInt(iter->second, &value)) {
+    DCHECK(value > 0);
+    *target = base::TimeDelta::FromSeconds(value);
+  }
+}
+
+}  // namespace
+
+MemoryStateUpdater::MemoryStateUpdater(
+    MemoryCoordinatorImpl* coordinator,
+    scoped_refptr<base::SingleThreadTaskRunner> task_runner)
+    : coordinator_(coordinator), task_runner_(task_runner) {
+  DCHECK(coordinator_);
+  InitializeParameters();
+  DCHECK(ValidateParameters());
+}
+
+MemoryStateUpdater::~MemoryStateUpdater() {}
+
+base::MemoryState MemoryStateUpdater::CalculateNextState() {
+  using MemoryState = base::MemoryState;
+
+  int available =
+      coordinator_->memory_monitor()->GetFreeMemoryUntilCriticalMB();
+
+  // TODO(chrisha): Move this histogram recording to a better place when
+  // https://codereview.chromium.org/2479673002/ is landed.
+  UMA_HISTOGRAM_MEMORY_LARGE_MB("Memory.Coordinator.FreeMemoryUntilCritical",
+                                available);
+
+  if (available <= 0)
+    return MemoryState::SUSPENDED;
+
+  auto current_state = coordinator_->GetGlobalMemoryState();
+  int expected_renderer_count = available / expected_renderer_size_;
+
+  switch (current_state) {
+    case MemoryState::NORMAL:
+      if (expected_renderer_count <= new_renderers_until_suspended_)
+        return MemoryState::SUSPENDED;
+      if (expected_renderer_count <= new_renderers_until_throttled_)
+        return MemoryState::THROTTLED;
+      return MemoryState::NORMAL;
+    case MemoryState::THROTTLED:
+      if (expected_renderer_count <= new_renderers_until_suspended_)
+        return MemoryState::SUSPENDED;
+      if (expected_renderer_count >= new_renderers_back_to_normal_)
+        return MemoryState::NORMAL;
+      return MemoryState::THROTTLED;
+    case MemoryState::SUSPENDED:
+      if (expected_renderer_count >= new_renderers_back_to_normal_)
+        return MemoryState::NORMAL;
+      if (expected_renderer_count >= new_renderers_back_to_throttled_)
+        return MemoryState::THROTTLED;
+      return MemoryState::SUSPENDED;
+    case MemoryState::UNKNOWN:
+      // Fall through
+    default:
+      NOTREACHED();
+      return MemoryState::UNKNOWN;
+  }
+}
+
+void MemoryStateUpdater::UpdateState() {
+  auto current_state = coordinator_->GetGlobalMemoryState();
+  auto next_state = CalculateNextState();
+  if (coordinator_->ChangeStateIfNeeded(current_state, next_state)) {
+    ScheduleUpdateState(minimum_transition_period_);
+  } else {
+    ScheduleUpdateState(monitoring_interval_);
+  }
+}
+
+void MemoryStateUpdater::ScheduleUpdateState(base::TimeDelta delta) {
+  update_state_closure_.Reset(base::Bind(&MemoryStateUpdater::UpdateState,
+                                         base::Unretained(this)));
+  task_runner_->PostDelayedTask(FROM_HERE, update_state_closure_.callback(),
+                                delta);
+}
+
+void MemoryStateUpdater::InitializeParameters() {
+  expected_renderer_size_ = kDefaultExpectedRendererSizeMB;
+  new_renderers_until_throttled_ = kDefaultNewRenderersUntilThrottled;
+  new_renderers_until_suspended_ = kDefaultNewRenderersUntilSuspended;
+  new_renderers_back_to_normal_ = kDefaultNewRenderersBackToNormal;
+  new_renderers_back_to_throttled_ = kDefaultNewRenderersBackToThrottled;
+  minimum_transition_period_ =
+      base::TimeDelta::FromSeconds(kDefaultMinimumTransitionPeriodSeconds);
+  monitoring_interval_ =
+      base::TimeDelta::FromSeconds(kDefaultMonitoringIntervalSeconds);
+
+  // Override default parameters with variations.
+  static constexpr char kMemoryCoordinatorV0Trial[] = "MemoryCoordinatorV0";
+  std::map<std::string, std::string> params;
+  variations::GetVariationParams(kMemoryCoordinatorV0Trial, &params);
+  SetIntVariationParameter(params, "expected_renderer_size",
+                           &expected_renderer_size_);
+  SetIntVariationParameter(params, "new_renderers_until_throttled",
+                           &new_renderers_until_throttled_);
+  SetIntVariationParameter(params, "new_renderers_until_suspended",
+                           &new_renderers_until_suspended_);
+  SetIntVariationParameter(params, "new_renderers_back_to_normal",
+                           &new_renderers_back_to_normal_);
+  SetIntVariationParameter(params, "new_renderers_back_to_throttled",
+                           &new_renderers_back_to_throttled_);
+  SetSecondsVariationParameter(params, "minimum_transition_period",
+                               &minimum_transition_period_);
+  SetSecondsVariationParameter(params, "monitoring_interval",
+                               &monitoring_interval_);
+}
+
+bool MemoryStateUpdater::ValidateParameters() {
+  return (new_renderers_until_throttled_ > new_renderers_until_suspended_) &&
+      (new_renderers_back_to_normal_ > new_renderers_back_to_throttled_) &&
+      (new_renderers_back_to_normal_ > new_renderers_until_throttled_) &&
+      (new_renderers_back_to_throttled_ > new_renderers_until_suspended_);
+}
+
+}  // namespace content
diff --git a/content/browser/memory/memory_state_updater.h b/content/browser/memory/memory_state_updater.h
new file mode 100644
index 0000000..2c8998a8
--- /dev/null
+++ b/content/browser/memory/memory_state_updater.h
@@ -0,0 +1,97 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_BROWSER_MEMORY_MEMORY_STATE_UPDATER_H_
+#define CONTENT_BROWSER_MEMORY_MEMORY_STATE_UPDATER_H_
+
+#include "base/cancelable_callback.h"
+#include "base/single_thread_task_runner.h"
+#include "base/time/time.h"
+#include "content/browser/memory/memory_coordinator_impl.h"
+#include "content/common/content_export.h"
+
+namespace content {
+
+// MemoryStateUpdater is an internal implementation of MemoryCoordinator
+// which uses a heuristic to determine a single global memory state.
+// In the current implementation browser process and renderer processes share
+// the global state; the memory coordinator will notify the global state to
+// all background renderers if the state has changed.
+//
+// State calculation:
+// MemoryStateUpdater uses followings to determine the global state:
+// * Compute "number of renderers which can be created until the system will
+//   be in a critical state". Call this N.
+//   (See memory_monitor.h for the definition of "critical")
+// * Covert N to a memory state using some thresholds/hysteresis for each state.
+//   Once a state is changed to a limited state, larger N will be needed to go
+//   back to a relaxed state. (e.g. THROTTLED -> NORMAL)
+// * Once a state is changed, it remains the same for a certain period of time.
+class CONTENT_EXPORT MemoryStateUpdater {
+ public:
+  // |coordinator| must outlive than this instance.
+  MemoryStateUpdater(MemoryCoordinatorImpl* coordinator,
+                     scoped_refptr<base::SingleThreadTaskRunner> task_runner);
+  ~MemoryStateUpdater();
+
+  // Calculates the next global state from the amount of free memory using
+  // a heuristic.
+  base::MemoryState CalculateNextState();
+
+  // Schedules a task to update the global state. The task will be executed
+  // after |delay| has passed.
+  void ScheduleUpdateState(base::TimeDelta delay);
+
+ private:
+  FRIEND_TEST_ALL_PREFIXES(MemoryCoordinatorImplTest, CalculateNextState);
+  FRIEND_TEST_ALL_PREFIXES(MemoryCoordinatorImplTest, UpdateState);
+  FRIEND_TEST_ALL_PREFIXES(MemoryCoordinatorImplTest, SetMemoryStateForTesting);
+  FRIEND_TEST_ALL_PREFIXES(MemoryCoordinatorImplTest, ForceSetGlobalState);
+
+  // Periodically called to update the global state.
+  void UpdateState();
+
+  MemoryCoordinatorImpl* coordinator_;
+  scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
+  base::CancelableClosure update_state_closure_;
+
+  // Sets up parameters for the heuristic.
+  void InitializeParameters();
+
+  // Validates parameters defined below.
+  bool ValidateParameters();
+
+  // Parameters to control the heuristic.
+
+  // The median size of a renderer on the current platform. This is used to
+  // convert the amount of free memory to an expected number of new renderers
+  // that could be started before hitting critical memory pressure.
+  int expected_renderer_size_;
+  // When in a NORMAL state and the potential number of new renderers drops
+  // below this level, the coordinator will transition to a THROTTLED state.
+  int new_renderers_until_throttled_;
+  // When in a NORMAL/THROTTLED state and the potential number of new renderers
+  // drops below this level, the coordinator will transition to a SUSPENDED
+  // state.
+  int new_renderers_until_suspended_;
+  // When in a THROTTLED/SUSPENDED state and the potential number of new
+  // renderers rises above this level, the coordinator will transition to a
+  // NORMAL state.
+  int new_renderers_back_to_normal_;
+  // When in a SUSPENDED state and the potential number of new renderers rises
+  // above this level, the coordinator will transition to a SUSPENDED state.
+  int new_renderers_back_to_throttled_;
+  // The memory coordinator stays in the same state at least this duration even
+  // when there are considerable changes in the amount of free memory to prevent
+  // thrashing.
+  base::TimeDelta minimum_transition_period_;
+  // The interval of checking the amount of free memory.
+  base::TimeDelta monitoring_interval_;
+
+  DISALLOW_COPY_AND_ASSIGN(MemoryStateUpdater);
+};
+
+}  // namespace content
+
+#endif  // CONTENT_BROWSER_MEMORY_MEMORY_STATE_UPDATER_H_
diff --git a/content/browser/payments/payment_app_manager_unittest.cc b/content/browser/payments/payment_app_manager_unittest.cc
index e67b57c..ccd8c6a 100644
--- a/content/browser/payments/payment_app_manager_unittest.cc
+++ b/content/browser/payments/payment_app_manager_unittest.cc
@@ -59,9 +59,8 @@
             new TestBrowserThreadBundle(TestBrowserThreadBundle::IO_MAINLOOP)),
         embedded_worker_helper_(new EmbeddedWorkerTestHelper(base::FilePath())),
         storage_partition_impl_(new StoragePartitionImpl(
-            embedded_worker_helper_->browser_context(), base::FilePath(),
-            nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
-            nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr)) {
+            embedded_worker_helper_->browser_context(),
+            base::FilePath(), nullptr)) {
 
     embedded_worker_helper_->context_wrapper()->set_storage_partition(
         storage_partition_impl_.get());
diff --git a/content/browser/quota/mock_quota_manager.cc b/content/browser/quota/mock_quota_manager.cc
index 7340797..65e197a 100644
--- a/content/browser/quota/mock_quota_manager.cc
+++ b/content/browser/quota/mock_quota_manager.cc
@@ -44,14 +44,13 @@
                    profile_path,
                    io_thread,
                    db_thread,
-                   special_storage_policy),
-      weak_factory_(this) {
-}
+                   special_storage_policy,
+                   storage::GetQuotaSettingsFunc()),
+      weak_factory_(this) {}
 
-void MockQuotaManager::GetUsageAndQuota(
-    const GURL& origin,
-    storage::StorageType type,
-    const GetUsageAndQuotaCallback& callback) {
+void MockQuotaManager::GetUsageAndQuota(const GURL& origin,
+                                        storage::StorageType type,
+                                        const UsageAndQuotaCallback& callback) {
   StorageInfo& info = usage_and_quota_map_[std::make_pair(origin, type)];
   callback.Run(storage::kQuotaStatusOk, info.usage, info.quota);
 }
diff --git a/content/browser/quota/mock_quota_manager.h b/content/browser/quota/mock_quota_manager.h
index 172b7e92..6289f319 100644
--- a/content/browser/quota/mock_quota_manager.h
+++ b/content/browser/quota/mock_quota_manager.h
@@ -56,7 +56,7 @@
   // a helper method MockQuotaManagerProxy::SetQuota().
   void GetUsageAndQuota(const GURL& origin,
                         storage::StorageType type,
-                        const GetUsageAndQuotaCallback& callback) override;
+                        const UsageAndQuotaCallback& callback) override;
 
   // Overrides QuotaManager's implementation with a canned implementation that
   // allows clients to set up the origin database that should be queried. This
diff --git a/content/browser/quota/mock_quota_manager_proxy.cc b/content/browser/quota/mock_quota_manager_proxy.cc
index 697577b..8ca0293 100644
--- a/content/browser/quota/mock_quota_manager_proxy.cc
+++ b/content/browser/quota/mock_quota_manager_proxy.cc
@@ -40,7 +40,7 @@
     base::SequencedTaskRunner* original_task_runner,
     const GURL& origin,
     StorageType type,
-    const QuotaManager::GetUsageAndQuotaCallback& callback) {
+    const QuotaManager::UsageAndQuotaCallback& callback) {
   if (mock_manager()) {
     mock_manager()->GetUsageAndQuota(origin, type, callback);
   }
diff --git a/content/browser/quota/mock_quota_manager_proxy.h b/content/browser/quota/mock_quota_manager_proxy.h
index e550a9d9..3f9ddab 100644
--- a/content/browser/quota/mock_quota_manager_proxy.h
+++ b/content/browser/quota/mock_quota_manager_proxy.h
@@ -41,7 +41,7 @@
       base::SequencedTaskRunner* original_task_runner,
       const GURL& origin,
       StorageType type,
-      const QuotaManager::GetUsageAndQuotaCallback& callback) override;
+      const QuotaManager::UsageAndQuotaCallback& callback) override;
 
   // Validates the |client_id| and updates the internal access count
   // which can be accessed via notify_storage_accessed_count().
diff --git a/content/browser/quota/quota_backend_impl_unittest.cc b/content/browser/quota/quota_backend_impl_unittest.cc
index 12e76e6..6054e32 100644
--- a/content/browser/quota/quota_backend_impl_unittest.cc
+++ b/content/browser/quota/quota_backend_impl_unittest.cc
@@ -70,7 +70,7 @@
   void GetUsageAndQuota(base::SequencedTaskRunner* original_task_runner,
                         const GURL& origin,
                         storage::StorageType type,
-                        const GetUsageAndQuotaCallback& callback) override {
+                        const UsageAndQuotaCallback& callback) override {
     callback.Run(storage::kQuotaStatusOk, usage_, quota_);
   }
 
diff --git a/content/browser/quota/quota_manager_unittest.cc b/content/browser/quota/quota_manager_unittest.cc
index 580d68e..3613ec8 100644
--- a/content/browser/quota/quota_manager_unittest.cc
+++ b/content/browser/quota/quota_manager_unittest.cc
@@ -44,7 +44,6 @@
 using storage::QuotaManager;
 using storage::QuotaStatusCode;
 using storage::StorageType;
-using storage::UsageAndQuota;
 using storage::UsageInfo;
 using storage::UsageInfoEntries;
 
@@ -59,42 +58,26 @@
 
 const int kAllClients = QuotaClient::kAllClientsMask;
 
+// Values in bytes.
 const int64_t kAvailableSpaceForApp = 13377331U;
-
-const int64_t kMinimumPreserveForSystem =
-    QuotaManager::kMinimumPreserveForSystem;
-const int kPerHostTemporaryPortion = QuotaManager::kPerHostTemporaryPortion;
+const int64_t kMustRemainAvailableForSystem = kAvailableSpaceForApp / 2;
+const int64_t kDefaultPoolSize = 1000;
+const int64_t kDefaultPerHostQuota = 200;
 
 const GURL kTestEvictionOrigin = GURL("http://test.eviction.policy/result");
 
 // Returns a deterministic value for the amount of available disk space.
 int64_t GetAvailableDiskSpaceForTest() {
-  return kAvailableSpaceForApp + kMinimumPreserveForSystem;
+  return kAvailableSpaceForApp + kMustRemainAvailableForSystem;
 }
 
-bool GetVolumeInfoForTests(const base::FilePath&,
-                           uint64_t* available, uint64_t* total) {
-  *available = static_cast<uint64_t>(GetAvailableDiskSpaceForTest());
-  *total = *available * 2;
-  return true;
+std::pair<int64_t, int64_t> GetVolumeInfoForTests(
+    const base::FilePath& unused) {
+  int64_t available = static_cast<uint64_t>(GetAvailableDiskSpaceForTest());
+  int64_t total = available * 2;
+  return std::make_pair(total, available);
 }
 
-class TestEvictionPolicy : public storage::QuotaEvictionPolicy {
- public:
-  TestEvictionPolicy() {}
-  ~TestEvictionPolicy() override {}
-
-  // Overridden from storage::QuotaEvictionPolicy:
-  void GetEvictionOrigin(const scoped_refptr<storage::SpecialStoragePolicy>&
-                             special_storage_policy,
-                         const std::set<GURL>& exceptions,
-                         const std::map<GURL, int64_t>& usage_map,
-                         int64_t global_quota,
-                         const storage::GetOriginCallback& callback) override {
-    callback.Run(kTestEvictionOrigin);
-  }
-};
-
 }  // namespace
 
 class QuotaManagerTest : public testing::Test {
@@ -126,7 +109,11 @@
     quota_manager_ = new QuotaManager(is_incognito, data_dir_.GetPath(),
                                       base::ThreadTaskRunnerHandle::Get().get(),
                                       base::ThreadTaskRunnerHandle::Get().get(),
-                                      mock_special_storage_policy_.get());
+                                      mock_special_storage_policy_.get(),
+                                      storage::GetQuotaSettingsFunc());
+    SetQuotaSettings(kDefaultPoolSize, kDefaultPerHostQuota,
+                     is_incognito ? INT64_C(0) : kMustRemainAvailableForSystem);
+
     // Don't (automatically) start the eviction for testing.
     quota_manager_->eviction_disabled_ = true;
     // Don't query the hard disk for remaining capacity.
@@ -173,21 +160,15 @@
                                  weak_factory_.GetWeakPtr()));
   }
 
-  void GetTemporaryGlobalQuota() {
-    quota_status_ = kQuotaStatusUnknown;
-    quota_ = -1;
-    quota_manager_->GetTemporaryGlobalQuota(
-        base::Bind(&QuotaManagerTest::DidGetQuota,
-                   weak_factory_.GetWeakPtr()));
-  }
-
-  void SetTemporaryGlobalQuota(int64_t new_quota) {
-    quota_status_ = kQuotaStatusUnknown;
-    quota_ = -1;
-    quota_manager_->SetTemporaryGlobalOverrideQuota(
-        new_quota,
-        base::Bind(&QuotaManagerTest::DidGetQuota,
-                   weak_factory_.GetWeakPtr()));
+  void SetQuotaSettings(int64_t pool_size,
+                        int64_t per_host_quota,
+                        int64_t must_remain_available) {
+    storage::QuotaSettings settings;
+    settings.pool_size = pool_size;
+    settings.per_host_quota = per_host_quota;
+    settings.must_remain_available = must_remain_available;
+    settings.refresh_interval = base::TimeDelta::Max();
+    quota_manager_->SetQuotaSettings(settings);
   }
 
   void GetPersistentHostQuota(const std::string& host) {
@@ -272,22 +253,21 @@
                    weak_factory_.GetWeakPtr()));
   }
 
-  void GetAvailableSpace() {
-    quota_status_ = kQuotaStatusUnknown;
+  void GetStorageCapacity() {
     available_space_ = -1;
-    quota_manager_->GetAvailableSpace(
-        base::Bind(&QuotaManagerTest::DidGetAvailableSpace,
-                   weak_factory_.GetWeakPtr()));
+    total_space_ = -1;
+    quota_manager_->GetStorageCapacity(base::Bind(
+        &QuotaManagerTest::DidGetStorageCapacity, weak_factory_.GetWeakPtr()));
   }
 
-  void GetUsageAndQuotaForEviction() {
+  void GetEvictionRoundInfo() {
     quota_status_ = kQuotaStatusUnknown;
-    usage_ = -1;
-    unlimited_usage_ = -1;
-    quota_ = -1;
+    settings_ = storage::QuotaSettings();
     available_space_ = -1;
-    quota_manager_->GetUsageAndQuotaForEviction(
-        base::Bind(&QuotaManagerTest::DidGetUsageAndQuotaForEviction,
+    total_space_ = -1;
+    usage_ = -1;
+    quota_manager_->GetEvictionRoundInfo(
+        base::Bind(&QuotaManagerTest::DidGetEvictionRoundInfo,
                    weak_factory_.GetWeakPtr()));
   }
 
@@ -297,12 +277,6 @@
     quota_manager_->GetCachedOrigins(type, origins);
   }
 
-  bool GetVolumeInfo(const base::FilePath& path,
-                     uint64_t* available_space,
-                     uint64_t* total_size) {
-    return QuotaManager::GetVolumeInfo(path, available_space, total_size);
-  }
-
   void NotifyStorageAccessed(QuotaClient* client,
                              const GURL& origin,
                              StorageType type) {
@@ -373,8 +347,8 @@
     quota_ = quota;
   }
 
-  void DidGetAvailableSpace(QuotaStatusCode status, int64_t available_space) {
-    quota_status_ = status;
+  void DidGetStorageCapacity(int64_t total_space, int64_t available_space) {
+    total_space_ = total_space;
     available_space_ = available_space;
   }
 
@@ -395,12 +369,17 @@
     quota_status_ = status;
   }
 
-  void DidGetUsageAndQuotaForEviction(QuotaStatusCode status,
-                                      const UsageAndQuota& usage_and_quota) {
+  void DidGetEvictionRoundInfo(QuotaStatusCode status,
+                               const storage::QuotaSettings& settings,
+                               int64_t available_space,
+                               int64_t total_space,
+                               int64_t global_usage,
+                               bool global_usage_is_complete) {
     quota_status_ = status;
-    limited_usage_ = usage_and_quota.global_limited_usage;
-    quota_ = usage_and_quota.quota;
-    available_space_ = usage_and_quota.available_disk_space;
+    settings_ = settings;
+    available_space_ = available_space;
+    total_space_ = total_space;
+    usage_ = global_usage;
   }
 
   void DidGetEvictionOrigin(const GURL& origin) {
@@ -445,6 +424,7 @@
   int64_t limited_usage() const { return limited_usage_; }
   int64_t unlimited_usage() const { return unlimited_usage_; }
   int64_t quota() const { return quota_; }
+  int64_t total_space() const { return total_space_; }
   int64_t available_space() const { return available_space_; }
   const GURL& eviction_origin() const { return eviction_origin_; }
   const std::set<GURL>& modified_origins() const { return modified_origins_; }
@@ -453,6 +433,7 @@
   const OriginInfoTableEntries& origin_info_entries() const {
     return origin_info_entries_;
   }
+  const storage::QuotaSettings& settings() const { return settings_; }
   base::FilePath profile_path() const { return data_dir_.GetPath(); }
   int status_callback_count() const { return status_callback_count_; }
   void reset_status_callback_count() { status_callback_count_ = 0; }
@@ -475,12 +456,14 @@
   int64_t limited_usage_;
   int64_t unlimited_usage_;
   int64_t quota_;
+  int64_t total_space_;
   int64_t available_space_;
   GURL eviction_origin_;
   std::set<GURL> modified_origins_;
   StorageType modified_origins_type_;
   QuotaTableEntries quota_entries_;
   OriginInfoTableEntries origin_info_entries_;
+  storage::QuotaSettings settings_;
   int status_callback_count_;
 
   int additional_callback_count_;
@@ -633,20 +616,17 @@
       QuotaClient::kFileSystem));
 
   // This time explicitly sets a temporary global quota.
-  SetTemporaryGlobalQuota(100);
-  base::RunLoop().RunUntilIdle();
-  EXPECT_EQ(kQuotaStatusOk, status());
-  EXPECT_EQ(100, quota());
+  const int kPoolSize = 100;
+  const int kPerHostQuota = 20;
+  SetQuotaSettings(kPoolSize, kPerHostQuota, kMustRemainAvailableForSystem);
 
   GetUsageAndQuotaForWebApps(GURL("http://foo.com/"), kTemp);
   base::RunLoop().RunUntilIdle();
   EXPECT_EQ(kQuotaStatusOk, status());
   EXPECT_EQ(10 + 20, usage());
 
-  const int kPerHostQuota = 100 / kPerHostTemporaryPortion;
-
   // The host's quota should be its full portion of the global quota
-  // since global usage is under the global quota.
+  // since there's plenty of diskspace.
   EXPECT_EQ(kPerHostQuota, quota());
 
   GetUsageAndQuotaForWebApps(GURL("http://bar.com/"), kTemp);
@@ -662,39 +642,39 @@
     { "http://bar.com/",              kTemp, 2 },
     { "http://bar.com/",              kPerm, 4 },
     { "http://unlimited/",            kPerm, 8 },
-    { "http://installed/",            kPerm, 16 },
   };
   static const MockOriginData kData2[] = {
     { "https://foo.com/",             kTemp, 128 },
     { "http://example.com/",          kPerm, 256 },
     { "http://unlimited/",            kTemp, 512 },
-    { "http://installed/",            kTemp, 1024 },
   };
   mock_special_storage_policy()->AddUnlimited(GURL("http://unlimited/"));
-  mock_special_storage_policy()->GrantQueryDiskSize(GURL("http://installed/"));
   RegisterClient(CreateClient(kData1, arraysize(kData1),
       QuotaClient::kFileSystem));
   RegisterClient(CreateClient(kData2, arraysize(kData2),
       QuotaClient::kDatabase));
 
-  const int64_t kTempQuotaBase =
-      GetAvailableDiskSpaceForTest() / kPerHostTemporaryPortion;
+  const int64_t kPoolSize = GetAvailableDiskSpaceForTest();
+  const int64_t kPerHostQuota = kPoolSize / 5;
+  SetQuotaSettings(kPoolSize, kPerHostQuota, kMustRemainAvailableForSystem);
 
   GetUsageAndQuotaForWebApps(GURL("http://foo.com/"), kTemp);
   base::RunLoop().RunUntilIdle();
   EXPECT_EQ(kQuotaStatusOk, status());
   EXPECT_EQ(1 + 128, usage());
+  EXPECT_EQ(kPerHostQuota, quota());
 
   GetUsageAndQuotaForWebApps(GURL("http://bar.com/"), kPerm);
   base::RunLoop().RunUntilIdle();
   EXPECT_EQ(kQuotaStatusOk, status());
   EXPECT_EQ(4, usage());
+  EXPECT_EQ(0, quota());
 
   GetUsageAndQuotaForWebApps(GURL("http://unlimited/"), kTemp);
   base::RunLoop().RunUntilIdle();
   EXPECT_EQ(kQuotaStatusOk, status());
   EXPECT_EQ(512, usage());
-  EXPECT_EQ(std::min(kAvailableSpaceForApp, kTempQuotaBase) + usage(), quota());
+  EXPECT_EQ(kAvailableSpaceForApp + usage(), quota());
 
   GetUsageAndQuotaForWebApps(GURL("http://unlimited/"), kPerm);
   base::RunLoop().RunUntilIdle();
@@ -702,33 +682,16 @@
   EXPECT_EQ(8, usage());
   EXPECT_EQ(kAvailableSpaceForApp + usage(), quota());
 
-  GetAvailableSpace();
-  base::RunLoop().RunUntilIdle();
-  EXPECT_EQ(kQuotaStatusOk, status());
-  EXPECT_LE(0, available_space());
-
-  GetUsageAndQuotaForWebApps(GURL("http://installed/"), kTemp);
-  base::RunLoop().RunUntilIdle();
-  EXPECT_EQ(kQuotaStatusOk, status());
-  EXPECT_EQ(1024, usage());
-  EXPECT_EQ(std::min(kAvailableSpaceForApp, kTempQuotaBase) + usage(), quota());
-
-  GetUsageAndQuotaForWebApps(GURL("http://installed/"), kPerm);
-  base::RunLoop().RunUntilIdle();
-  EXPECT_EQ(kQuotaStatusOk, status());
-  EXPECT_EQ(16, usage());
-  EXPECT_EQ(usage(), quota());  // Over-budget case.
-
   GetGlobalUsage(kTemp);
   base::RunLoop().RunUntilIdle();
   EXPECT_EQ(kQuotaStatusOk, status());
-  EXPECT_EQ(1 + 2 + 128 + 512 + 1024, usage());
+  EXPECT_EQ(1 + 2 + 128 + 512, usage());
   EXPECT_EQ(512, unlimited_usage());
 
   GetGlobalUsage(kPerm);
   base::RunLoop().RunUntilIdle();
   EXPECT_EQ(kQuotaStatusOk, status());
-  EXPECT_EQ(4 + 8 + 16 + 256, usage());
+  EXPECT_EQ(4 + 8 + 256, usage());
   EXPECT_EQ(8, unlimited_usage());
 }
 
@@ -781,10 +744,10 @@
   };
   RegisterClient(CreateClient(kData, arraysize(kData),
       QuotaClient::kFileSystem));
-  SetTemporaryGlobalQuota(100);
-  base::RunLoop().RunUntilIdle();
 
-  const int kPerHostQuota = 100 / QuotaManager::kPerHostTemporaryPortion;
+  const int kPoolSize = 100;
+  const int kPerHostQuota = 20;
+  SetQuotaSettings(kPoolSize, kPerHostQuota, kMustRemainAvailableForSystem);
 
   GetUsageAndQuotaForWebApps(GURL("http://foo.com/"), kTemp);
   GetUsageAndQuotaForWebApps(GURL("http://foo.com/"), kTemp);
@@ -815,8 +778,9 @@
   };
   RegisterClient(CreateClient(kData, arraysize(kData),
       QuotaClient::kFileSystem));
-  SetTemporaryGlobalQuota(100);
-  base::RunLoop().RunUntilIdle();
+  const int kPoolSize = 100;
+  const int kPerHostQuota = 20;
+  SetQuotaSettings(kPoolSize, kPerHostQuota, kMustRemainAvailableForSystem);
 
   set_additional_callback_count(0);
   GetUsageAndQuotaForWebApps(GURL("http://foo.com/"), kTemp);
@@ -842,22 +806,28 @@
   };
   RegisterClient(CreateClient(kData, arraysize(kData),
       QuotaClient::kFileSystem));
-  SetTemporaryGlobalQuota(100);
-  base::RunLoop().RunUntilIdle();
+  const int kPoolSize = 100;
+  const int kPerHostQuota = 20;
+  SetQuotaSettings(kPoolSize, kPerHostQuota, kMustRemainAvailableForSystem);
 
-  const int kPerHostQuota = 100 / QuotaManager::kPerHostTemporaryPortion;
+  // Provided diskspace is not tight, global usage does not affect the
+  // quota calculations for an individual origin, so despite global usage
+  // in excess of our poolsize, we still get the nominal quota value.
+  GetStorageCapacity();
+  base::RunLoop().RunUntilIdle();
+  EXPECT_LE(kMustRemainAvailableForSystem, available_space());
 
   GetUsageAndQuotaForWebApps(GURL("http://usage1/"), kTemp);
   base::RunLoop().RunUntilIdle();
   EXPECT_EQ(kQuotaStatusOk, status());
   EXPECT_EQ(1, usage());
-  EXPECT_EQ(1, quota());  // should be clamped to our current usage
+  EXPECT_EQ(kPerHostQuota, quota());
 
   GetUsageAndQuotaForWebApps(GURL("http://usage10/"), kTemp);
   base::RunLoop().RunUntilIdle();
   EXPECT_EQ(kQuotaStatusOk, status());
   EXPECT_EQ(10, usage());
-  EXPECT_EQ(10, quota());
+  EXPECT_EQ(kPerHostQuota, quota());
 
   GetUsageAndQuotaForWebApps(GURL("http://usage200/"), kTemp);
   base::RunLoop().RunUntilIdle();
@@ -878,17 +848,14 @@
   RegisterClient(client);
 
   // Test when not overbugdet.
-  SetTemporaryGlobalQuota(1000);
-  base::RunLoop().RunUntilIdle();
+  const int kPerHostQuotaFor1000 = 200;
+  SetQuotaSettings(1000, kPerHostQuotaFor1000, kMustRemainAvailableForSystem);
 
   GetGlobalUsage(kTemp);
   base::RunLoop().RunUntilIdle();
   EXPECT_EQ(10 + 50 + 4000, usage());
   EXPECT_EQ(4000, unlimited_usage());
 
-  const int kPerHostQuotaFor1000 =
-      1000 / QuotaManager::kPerHostTemporaryPortion;
-
   GetUsageAndQuotaForWebApps(GURL("http://usage10/"), kTemp);
   base::RunLoop().RunUntilIdle();
   EXPECT_EQ(kQuotaStatusOk, status());
@@ -914,11 +881,8 @@
   EXPECT_EQ(QuotaManager::kNoLimit, quota());
 
   // Test when overbugdet.
-  SetTemporaryGlobalQuota(100);
-  base::RunLoop().RunUntilIdle();
-
-  const int kPerHostQuotaFor100 =
-      100 / QuotaManager::kPerHostTemporaryPortion;
+  const int kPerHostQuotaFor100 = 20;
+  SetQuotaSettings(100, kPerHostQuotaFor100, kMustRemainAvailableForSystem);
 
   GetUsageAndQuotaForWebApps(GURL("http://usage10/"), kTemp);
   base::RunLoop().RunUntilIdle();
@@ -957,7 +921,7 @@
   base::RunLoop().RunUntilIdle();
   EXPECT_EQ(kQuotaStatusOk, status());
   EXPECT_EQ(10, usage());
-  EXPECT_EQ(10, quota());  // should be clamped to our current usage
+  EXPECT_EQ(kPerHostQuotaFor100, quota());
 
   GetUsageAndQuotaForWebApps(GURL("http://usage50/"), kTemp);
   base::RunLoop().RunUntilIdle();
@@ -1043,14 +1007,7 @@
   EXPECT_EQ(0, usage());
   EXPECT_EQ(100, quota());
 
-  // For installed app GetUsageAndQuotaForWebApps returns the capped quota.
-  mock_special_storage_policy()->GrantQueryDiskSize(GURL("http://installed/"));
-  SetPersistentHostQuota("installed", kAvailableSpaceForApp + 100);
-  GetUsageAndQuotaForWebApps(GURL("http://installed/"), kPerm);
-  base::RunLoop().RunUntilIdle();
-  EXPECT_EQ(kAvailableSpaceForApp, quota());
-
-  // Ditto for unlimited apps.
+  // The actual space avaialble is given to 'unlimited' origins as their quota.
   mock_special_storage_policy()->AddUnlimited(GURL("http://unlimited/"));
   GetUsageAndQuotaForWebApps(GURL("http://unlimited/"), kPerm);
   base::RunLoop().RunUntilIdle();
@@ -1072,22 +1029,14 @@
   EXPECT_LE(kAvailableSpaceForApp,
             QuotaManager::kSyncableStorageDefaultHostQuota);
 
-  // For installed apps the quota manager should return
+  // For unlimited origins the quota manager should return
   // kAvailableSpaceForApp as syncable quota (because of the pre-condition).
-  mock_special_storage_policy()->GrantQueryDiskSize(GURL("http://installed/"));
-  GetUsageAndQuotaForWebApps(GURL("http://installed/"), kSync);
+  mock_special_storage_policy()->AddUnlimited(GURL("http://unlimited/"));
+  GetUsageAndQuotaForWebApps(GURL("http://unlimited/"), kSync);
   base::RunLoop().RunUntilIdle();
   EXPECT_EQ(kQuotaStatusOk, status());
   EXPECT_EQ(0, usage());
   EXPECT_EQ(kAvailableSpaceForApp, quota());
-
-  // If it's not installed (which shouldn't happen in real case) it
-  // should just return the default host quota for syncable.
-  GetUsageAndQuotaForWebApps(GURL("http://foo/"), kSync);
-  base::RunLoop().RunUntilIdle();
-  EXPECT_EQ(kQuotaStatusOk, status());
-  EXPECT_EQ(0, usage());
-  EXPECT_EQ(QuotaManager::kSyncableStorageDefaultHostQuota, quota());
 }
 
 TEST_F(QuotaManagerTest, GetPersistentUsageAndQuota_MultiOrigins) {
@@ -1294,22 +1243,13 @@
   EXPECT_EQ(predelete_host_pers, usage());
 }
 
-TEST_F(QuotaManagerTest, GetAvailableSpaceTest) {
-  GetAvailableSpace();
+TEST_F(QuotaManagerTest, GetStorageCapacity) {
+  GetStorageCapacity();
   base::RunLoop().RunUntilIdle();
-  EXPECT_EQ(kQuotaStatusOk, status());
+  EXPECT_LE(0, total_space());
   EXPECT_LE(0, available_space());
 }
 
-TEST_F(QuotaManagerTest, SetTemporaryStorageEvictionPolicy) {
-  quota_manager()->SetTemporaryStorageEvictionPolicy(
-      base::WrapUnique(new TestEvictionPolicy));
-
-  GetEvictionOrigin(kTemp);
-  base::RunLoop().RunUntilIdle();
-  EXPECT_EQ(kTestEvictionOrigin, eviction_origin());
-}
-
 TEST_F(QuotaManagerTest, EvictOriginData) {
   static const MockOriginData kData1[] = {
     { "http://foo.com/",   kTemp,     1 },
@@ -1528,7 +1468,7 @@
   EXPECT_EQ(predelete_host_pers, usage());
 }
 
-TEST_F(QuotaManagerTest, GetUsageAndQuotaForEviction) {
+TEST_F(QuotaManagerTest, GetEvictionRoundInfo) {
   static const MockOriginData kData[] = {
     { "http://foo.com/",   kTemp,       1 },
     { "http://foo.com:1/", kTemp,      20 },
@@ -1541,14 +1481,15 @@
       QuotaClient::kFileSystem);
   RegisterClient(client);
 
-  SetTemporaryGlobalQuota(10000000);
-  base::RunLoop().RunUntilIdle();
+  const int kPoolSize = 10000000;
+  const int kPerHostQuota = kPoolSize / 5;
+  SetQuotaSettings(kPoolSize, kPerHostQuota, kMustRemainAvailableForSystem);
 
-  GetUsageAndQuotaForEviction();
+  GetEvictionRoundInfo();
   base::RunLoop().RunUntilIdle();
   EXPECT_EQ(kQuotaStatusOk, status());
-  EXPECT_EQ(21, limited_usage());
-  EXPECT_EQ(10000000, quota());
+  EXPECT_EQ(21, usage());
+  EXPECT_EQ(kPoolSize, settings().pool_size);
   EXPECT_LE(0, available_space());
 }
 
@@ -1801,16 +1742,19 @@
   GetCachedOrigins(kTemp, &origins);
   EXPECT_TRUE(origins.empty());
 
-  // No matter how we make queries the quota manager tries to cache all
-  // the origins at startup.
   GetHostUsage("a.com", kTemp);
   base::RunLoop().RunUntilIdle();
   GetCachedOrigins(kTemp, &origins);
-  EXPECT_EQ(3U, origins.size());
+  EXPECT_EQ(2U, origins.size());
 
   GetHostUsage("b.com", kTemp);
   base::RunLoop().RunUntilIdle();
   GetCachedOrigins(kTemp, &origins);
+  EXPECT_EQ(2U, origins.size());
+
+  GetHostUsage("c.com", kTemp);
+  base::RunLoop().RunUntilIdle();
+  GetCachedOrigins(kTemp, &origins);
   EXPECT_EQ(3U, origins.size());
 
   GetCachedOrigins(kPerm, &origins);
@@ -2267,44 +2211,44 @@
   RegisterClient(CreateClient(kData, arraysize(kData),
       QuotaClient::kFileSystem));
 
+  // Query global usage to warmup the usage tracker caching.
+  GetGlobalUsage(kTemp);
+  GetGlobalUsage(kPerm);
+  base::RunLoop().RunUntilIdle();
+
   GetUsageAndQuotaForWebApps(GURL("http://foo.com/"), kPerm);
   base::RunLoop().RunUntilIdle();
   EXPECT_EQ(kQuotaStatusOk, status());
   EXPECT_EQ(80, usage());
   EXPECT_EQ(0, quota());
 
-  SetTemporaryGlobalQuota(100);
+  const int kPoolSize = 1000;
+  const int kPerHostQuota = kPoolSize / 5;
+  SetQuotaSettings(kPoolSize, kPerHostQuota, INT64_C(0));
+
+  GetStorageCapacity();
+  base::RunLoop().RunUntilIdle();
+  EXPECT_EQ(kPoolSize, total_space());
+  EXPECT_EQ(kPoolSize - 80 - 10, available_space());
+
   GetUsageAndQuotaForWebApps(GURL("http://foo.com/"), kTemp);
   base::RunLoop().RunUntilIdle();
   EXPECT_EQ(kQuotaStatusOk, status());
   EXPECT_EQ(10, usage());
-  EXPECT_LE(std::min(static_cast<int64_t>(100 / kPerHostTemporaryPortion),
-                     QuotaManager::kIncognitoDefaultQuotaLimit),
-            quota());
+  EXPECT_LE(kPerHostQuota, quota());
 
   mock_special_storage_policy()->AddUnlimited(GURL("http://foo.com/"));
   GetUsageAndQuotaForWebApps(GURL("http://foo.com/"), kPerm);
   base::RunLoop().RunUntilIdle();
   EXPECT_EQ(kQuotaStatusOk, status());
   EXPECT_EQ(80, usage());
-  EXPECT_EQ(QuotaManager::kIncognitoDefaultQuotaLimit, quota());
+  EXPECT_EQ(available_space() + usage(), quota());
 
   GetUsageAndQuotaForWebApps(GURL("http://foo.com/"), kTemp);
   base::RunLoop().RunUntilIdle();
   EXPECT_EQ(kQuotaStatusOk, status());
   EXPECT_EQ(10, usage());
-  EXPECT_EQ(QuotaManager::kIncognitoDefaultQuotaLimit, quota());
-}
-
-TEST_F(QuotaManagerTest, GetVolumeInfo) {
-  // We aren't actually testing that it's correct, just that it's sane.
-  base::FilePath tmp_dir;
-  ASSERT_TRUE(base::GetTempDir(&tmp_dir));
-  uint64_t available_space = 0;
-  uint64_t total_size = 0;
-  EXPECT_TRUE(GetVolumeInfo(tmp_dir, &available_space, &total_size));
-  EXPECT_GT(available_space, 0u) << tmp_dir.value();
-  EXPECT_GT(total_size, 0u) << tmp_dir.value();
+  EXPECT_EQ(available_space() + usage(), quota());
 }
 
 }  // namespace content
diff --git a/content/browser/quota/quota_temporary_storage_evictor_unittest.cc b/content/browser/quota/quota_temporary_storage_evictor_unittest.cc
index 989c63d..ef417c708 100644
--- a/content/browser/quota/quota_temporary_storage_evictor_unittest.cc
+++ b/content/browser/quota/quota_temporary_storage_evictor_unittest.cc
@@ -21,7 +21,6 @@
 
 using storage::QuotaTemporaryStorageEvictor;
 using storage::StorageType;
-using storage::UsageAndQuota;
 
 namespace content {
 
@@ -31,15 +30,14 @@
 
 class MockQuotaEvictionHandler : public storage::QuotaEvictionHandler {
  public:
-  explicit MockQuotaEvictionHandler(QuotaTemporaryStorageEvictorTest *test)
-      : quota_(0),
-        available_space_(0),
+  explicit MockQuotaEvictionHandler(QuotaTemporaryStorageEvictorTest* test)
+      : available_space_(0),
         error_on_evict_origin_data_(false),
         error_on_get_usage_and_quota_(false) {}
 
   void EvictOriginData(const GURL& origin,
                        StorageType type,
-                       const EvictOriginDataCallback& callback) override {
+                       const storage::StatusCallback& callback) override {
     if (error_on_evict_origin_data_) {
       callback.Run(storage::kQuotaErrorInvalidModification);
       return;
@@ -50,22 +48,17 @@
     callback.Run(storage::kQuotaStatusOk);
   }
 
-  void AsyncGetVolumeInfo(const VolumeInfoCallback& callback) override {
-    uint64_t available = static_cast<uint64_t>(available_space_);
-    uint64_t total = (1024 * 1024 * 1024) + (2 * available);  // 1G plus some.
-    callback.Run(true, available, total);
-  }
-
-  void GetUsageAndQuotaForEviction(
-      const UsageAndQuotaCallback& callback) override {
+  void GetEvictionRoundInfo(
+      const EvictionRoundInfoCallback& callback) override {
     if (error_on_get_usage_and_quota_) {
-      callback.Run(storage::kQuotaErrorInvalidAccess, UsageAndQuota());
+      callback.Run(storage::kQuotaErrorAbort, storage::QuotaSettings(), 0, 0, 0,
+                   false);
       return;
     }
     if (!task_for_get_usage_and_quota_.is_null())
       task_for_get_usage_and_quota_.Run();
-    UsageAndQuota quota_and_usage(-1, GetUsage(), quota_, available_space_);
-    callback.Run(storage::kQuotaStatusOk, quota_and_usage);
+    callback.Run(storage::kQuotaStatusOk, settings_, available_space_,
+                 available_space_ * 2, GetUsage(), true);
   }
 
   void GetEvictionOrigin(StorageType type,
@@ -86,7 +79,13 @@
     return total_usage;
   }
 
-  void set_quota(int64_t quota) { quota_ = quota; }
+  const storage::QuotaSettings& settings() const { return settings_; }
+  void SetPoolSize(int64_t pool_size) {
+    settings_.pool_size = pool_size;
+    settings_.per_host_quota = pool_size / 5;
+    settings_.must_remain_available = pool_size / 5;
+    settings_.refresh_interval = base::TimeDelta::Max();
+  }
   void set_available_space(int64_t available_space) {
     available_space_ = available_space;
   }
@@ -130,7 +129,7 @@
     return origin_usage;
   }
 
-  int64_t quota_;
+  storage::QuotaSettings settings_;
   int64_t available_space_;
   std::list<GURL> origin_order_;
   std::map<GURL, int64_t> origins_;
@@ -169,20 +168,20 @@
       int expected_usage_after_second) {
     EXPECT_GE(4, num_get_usage_and_quota_for_eviction_);
     switch (num_get_usage_and_quota_for_eviction_) {
-    case 2:
-      EXPECT_EQ(expected_usage_after_first,
-                quota_eviction_handler()->GetUsage());
-      if (!origin_to_be_added.first.is_empty())
-        quota_eviction_handler()->AddOrigin(origin_to_be_added.first,
-                                            origin_to_be_added.second);
-      if (!origin_to_be_accessed.is_empty())
-        quota_eviction_handler()->AccessOrigin(origin_to_be_accessed);
-      break;
-    case 3:
-      EXPECT_EQ(expected_usage_after_second,
-                quota_eviction_handler()->GetUsage());
-      temporary_storage_evictor()->set_repeated_eviction(false);
-      break;
+      case 2:
+        EXPECT_EQ(expected_usage_after_first,
+                  quota_eviction_handler()->GetUsage());
+        if (!origin_to_be_added.first.is_empty())
+          quota_eviction_handler()->AddOrigin(origin_to_be_added.first,
+                                              origin_to_be_added.second);
+        if (!origin_to_be_accessed.is_empty())
+          quota_eviction_handler()->AccessOrigin(origin_to_be_accessed);
+        break;
+      case 3:
+        EXPECT_EQ(expected_usage_after_second,
+                  quota_eviction_handler()->GetUsage());
+        temporary_storage_evictor()->timer_disabled_for_testing_ = true;
+        break;
     }
     ++num_get_usage_and_quota_for_eviction_;
   }
@@ -201,36 +200,19 @@
     return temporary_storage_evictor()->statistics_;
   }
 
-  void set_repeated_eviction(bool repeated_eviction) const {
-    return temporary_storage_evictor_->set_repeated_eviction(repeated_eviction);
+  void disable_timer_for_testing() const {
+    temporary_storage_evictor_->timer_disabled_for_testing_ = true;
   }
 
   int num_get_usage_and_quota_for_eviction() const {
     return num_get_usage_and_quota_for_eviction_;
   }
 
-  int64_t default_min_available_disk_space_to_start_eviction() const {
-    return 1000 * 1000 * 500;
-  }
-
-  void set_min_available_disk_space_to_start_eviction(int64_t value) const {
-    temporary_storage_evictor_->set_min_available_disk_space_to_start_eviction(
-        value);
-  }
-
-  void reset_min_available_disk_space_to_start_eviction() const {
-    temporary_storage_evictor_->
-        reset_min_available_disk_space_to_start_eviction();
-  }
-
   base::MessageLoop message_loop_;
   std::unique_ptr<MockQuotaEvictionHandler> quota_eviction_handler_;
   std::unique_ptr<QuotaTemporaryStorageEvictor> temporary_storage_evictor_;
-
   int num_get_usage_and_quota_for_eviction_;
-
   base::WeakPtrFactory<QuotaTemporaryStorageEvictorTest> weak_factory_;
-
   DISALLOW_COPY_AND_ASSIGN(QuotaTemporaryStorageEvictorTest);
 };
 
@@ -238,10 +220,10 @@
   quota_eviction_handler()->AddOrigin(GURL("http://www.z.com"), 3000);
   quota_eviction_handler()->AddOrigin(GURL("http://www.y.com"), 200);
   quota_eviction_handler()->AddOrigin(GURL("http://www.x.com"), 500);
-  quota_eviction_handler()->set_quota(4000);
+  quota_eviction_handler()->SetPoolSize(4000);
   quota_eviction_handler()->set_available_space(1000000000);
   EXPECT_EQ(3000 + 200 + 500, quota_eviction_handler()->GetUsage());
-  set_repeated_eviction(false);
+  disable_timer_for_testing();
   temporary_storage_evictor()->Start();
   base::RunLoop().RunUntilIdle();
   EXPECT_EQ(200 + 500, quota_eviction_handler()->GetUsage());
@@ -258,10 +240,10 @@
   quota_eviction_handler()->AddOrigin(GURL("http://www.y.com"), 2900);
   quota_eviction_handler()->AddOrigin(GURL("http://www.x.com"), 450);
   quota_eviction_handler()->AddOrigin(GURL("http://www.w.com"), 400);
-  quota_eviction_handler()->set_quota(4000);
+  quota_eviction_handler()->SetPoolSize(4000);
   quota_eviction_handler()->set_available_space(1000000000);
   EXPECT_EQ(20 + 2900 + 450 + 400, quota_eviction_handler()->GetUsage());
-  set_repeated_eviction(false);
+  disable_timer_for_testing();
   temporary_storage_evictor()->Start();
   base::RunLoop().RunUntilIdle();
   EXPECT_EQ(450 + 400, quota_eviction_handler()->GetUsage());
@@ -285,7 +267,7 @@
   quota_eviction_handler()->AddOrigin(GURL("http://www.c.com"), c_size);
   quota_eviction_handler()->AddOrigin(GURL("http://www.b.com"), b_size);
   quota_eviction_handler()->AddOrigin(GURL("http://www.a.com"), a_size);
-  quota_eviction_handler()->set_quota(1000);
+  quota_eviction_handler()->SetPoolSize(1000);
   quota_eviction_handler()->set_available_space(1000000000);
   quota_eviction_handler()->set_task_for_get_usage_and_quota(
       base::Bind(&QuotaTemporaryStorageEvictorTest::TaskForRepeatedEvictionTest,
@@ -318,14 +300,14 @@
   quota_eviction_handler()->AddOrigin(GURL("http://www.c.com"), c_size);
   quota_eviction_handler()->AddOrigin(GURL("http://www.b.com"), b_size);
   quota_eviction_handler()->AddOrigin(GURL("http://www.a.com"), a_size);
-  quota_eviction_handler()->set_quota(1000);
+  quota_eviction_handler()->SetPoolSize(1000);
   quota_eviction_handler()->set_available_space(1000000000);
   quota_eviction_handler()->set_task_for_get_usage_and_quota(
       base::Bind(&QuotaTemporaryStorageEvictorTest::TaskForRepeatedEvictionTest,
                  weak_factory_.GetWeakPtr(), std::make_pair(GURL(), 0), GURL(),
                  initial_total_size - d_size, initial_total_size - d_size));
   EXPECT_EQ(initial_total_size, quota_eviction_handler()->GetUsage());
-  set_repeated_eviction(true);
+  // disable_timer_for_testing();
   temporary_storage_evictor()->Start();
   base::RunLoop().RunUntilIdle();
   EXPECT_EQ(initial_total_size - d_size, quota_eviction_handler()->GetUsage());
@@ -350,7 +332,7 @@
   quota_eviction_handler()->AddOrigin(GURL("http://www.c.com"), c_size);
   quota_eviction_handler()->AddOrigin(GURL("http://www.b.com"), b_size);
   quota_eviction_handler()->AddOrigin(GURL("http://www.a.com"), a_size);
-  quota_eviction_handler()->set_quota(1000);
+  quota_eviction_handler()->SetPoolSize(1000);
   quota_eviction_handler()->set_available_space(1000000000);
   quota_eviction_handler()->set_task_for_get_usage_and_quota(
       base::Bind(&QuotaTemporaryStorageEvictorTest::TaskForRepeatedEvictionTest,
@@ -374,17 +356,18 @@
 }
 
 TEST_F(QuotaTemporaryStorageEvictorTest, DiskSpaceNonEvictionTest) {
-  quota_eviction_handler()->AddOrigin(GURL("http://www.z.com"), 414);
-  quota_eviction_handler()->AddOrigin(GURL("http://www.x.com"), 450);
-  quota_eviction_handler()->set_quota(10000);
+  // If we're using so little that evicting all of it wouldn't
+  // do enough to alleviate a diskspace shortage, we don't evict.
+  quota_eviction_handler()->AddOrigin(GURL("http://www.z.com"), 10);
+  quota_eviction_handler()->AddOrigin(GURL("http://www.x.com"), 20);
+  quota_eviction_handler()->SetPoolSize(10000);
   quota_eviction_handler()->set_available_space(
-      default_min_available_disk_space_to_start_eviction() - 350);
-  EXPECT_EQ(414 + 450, quota_eviction_handler()->GetUsage());
-  reset_min_available_disk_space_to_start_eviction();
-  set_repeated_eviction(false);
+      quota_eviction_handler()->settings().must_remain_available - 350);
+  EXPECT_EQ(10 + 20, quota_eviction_handler()->GetUsage());
+  disable_timer_for_testing();
   temporary_storage_evictor()->Start();
   base::RunLoop().RunUntilIdle();
-  EXPECT_EQ(414 + 450, quota_eviction_handler()->GetUsage());
+  EXPECT_EQ(10 + 20, quota_eviction_handler()->GetUsage());
 
   EXPECT_EQ(0, statistics().num_errors_on_evicting_origin);
   EXPECT_EQ(0, statistics().num_errors_on_getting_usage_and_quota);
@@ -398,13 +381,11 @@
   quota_eviction_handler()->AddOrigin(GURL("http://www.y.com"), 120);
   quota_eviction_handler()->AddOrigin(GURL("http://www.x.com"), 150);
   quota_eviction_handler()->AddOrigin(GURL("http://www.w.com"), 300);
-  quota_eviction_handler()->set_quota(10000);
+  quota_eviction_handler()->SetPoolSize(10000);
   quota_eviction_handler()->set_available_space(
-      default_min_available_disk_space_to_start_eviction() - 350);
+      quota_eviction_handler()->settings().must_remain_available - 350);
   EXPECT_EQ(294 + 120 + 150 + 300, quota_eviction_handler()->GetUsage());
-  set_min_available_disk_space_to_start_eviction(
-      default_min_available_disk_space_to_start_eviction());
-  set_repeated_eviction(false);
+  disable_timer_for_testing();
   temporary_storage_evictor()->Start();
   base::RunLoop().RunUntilIdle();
   EXPECT_EQ(150 + 300, quota_eviction_handler()->GetUsage());
diff --git a/content/browser/quota/storage_monitor_unittest.cc b/content/browser/quota/storage_monitor_unittest.cc
index 7cbeb91..12512cf2b 100644
--- a/content/browser/quota/storage_monitor_unittest.cc
+++ b/content/browser/quota/storage_monitor_unittest.cc
@@ -68,7 +68,8 @@
                      base::FilePath(),
                      base::ThreadTaskRunnerHandle::Get().get(),
                      base::ThreadTaskRunnerHandle::Get().get(),
-                     special_storage_policy),
+                     special_storage_policy,
+                     storage::GetQuotaSettingsFunc()),
         callback_usage_(0),
         callback_quota_(0),
         callback_status_(kQuotaStatusOk),
@@ -88,7 +89,7 @@
   void GetUsageAndQuotaForWebApps(
       const GURL& origin,
       StorageType type,
-      const GetUsageAndQuotaCallback& callback) override {
+      const UsageAndQuotaCallback& callback) override {
     if (initialized_)
       callback.Run(callback_status_, callback_usage_, callback_quota_);
     else
@@ -103,7 +104,7 @@
   int64_t callback_quota_;
   QuotaStatusCode callback_status_;
   bool initialized_;
-  GetUsageAndQuotaCallback delayed_callback_;
+  UsageAndQuotaCallback delayed_callback_;
 };
 
 }  // namespace
@@ -649,7 +650,8 @@
     storage_policy_ = new MockSpecialStoragePolicy();
     quota_manager_ = new QuotaManager(
         false, data_dir_.GetPath(), base::ThreadTaskRunnerHandle::Get().get(),
-        base::ThreadTaskRunnerHandle::Get().get(), storage_policy_.get());
+        base::ThreadTaskRunnerHandle::Get().get(), storage_policy_.get(),
+        storage::GetQuotaSettingsFunc());
 
     client_ = new MockStorageClient(quota_manager_->proxy(),
                                     NULL,
diff --git a/content/browser/renderer_host/render_process_host_impl.cc b/content/browser/renderer_host/render_process_host_impl.cc
index 9ae4ccf..5948e63c 100644
--- a/content/browser/renderer_host/render_process_host_impl.cc
+++ b/content/browser/renderer_host/render_process_host_impl.cc
@@ -1074,7 +1074,7 @@
       browser_context->GetResourceContext()->GetMediaDeviceIDSalt());
   AddFilter(audio_renderer_host_.get());
   AddFilter(
-      new MidiHost(GetID(), BrowserMainLoop::GetInstance()->midi_manager()));
+      new MidiHost(GetID(), BrowserMainLoop::GetInstance()->midi_service()));
   AddFilter(new AppCacheDispatcherHost(
       storage_partition_impl_->GetAppCacheService(), GetID()));
   AddFilter(new ClipboardMessageFilter(blob_storage_context));
diff --git a/content/browser/service_worker/service_worker_storage.cc b/content/browser/service_worker/service_worker_storage.cc
index adfca9c..b9730196 100644
--- a/content/browser/service_worker/service_worker_storage.cc
+++ b/content/browser/service_worker/service_worker_storage.cc
@@ -627,11 +627,14 @@
     const GURL& origin,
     const std::vector<std::pair<std::string, std::string>>& key_value_pairs,
     const StatusCallback& callback) {
-  DCHECK(state_ == INITIALIZED || state_ == DISABLED) << state_;
-  if (IsDisabled()) {
-    RunSoon(FROM_HERE, base::Bind(callback, SERVICE_WORKER_ERROR_ABORT));
+  if (!LazyInitialize(base::Bind(&ServiceWorkerStorage::StoreUserData,
+                                 weak_factory_.GetWeakPtr(), registration_id,
+                                 origin, key_value_pairs, callback))) {
+    if (state_ != INITIALIZING)
+      RunSoon(FROM_HERE, base::Bind(callback, SERVICE_WORKER_ERROR_ABORT));
     return;
   }
+  DCHECK_EQ(INITIALIZED, state_);
 
   if (registration_id == kInvalidServiceWorkerRegistrationId ||
       key_value_pairs.empty()) {
@@ -657,12 +660,16 @@
 void ServiceWorkerStorage::GetUserData(int64_t registration_id,
                                        const std::vector<std::string>& keys,
                                        const GetUserDataCallback& callback) {
-  DCHECK(state_ == INITIALIZED || state_ == DISABLED) << state_;
-  if (IsDisabled()) {
-    RunSoon(FROM_HERE, base::Bind(callback, std::vector<std::string>(),
-                                  SERVICE_WORKER_ERROR_ABORT));
+  if (!LazyInitialize(base::Bind(&ServiceWorkerStorage::GetUserData,
+                                 weak_factory_.GetWeakPtr(), registration_id,
+                                 keys, callback))) {
+    if (state_ != INITIALIZING) {
+      RunSoon(FROM_HERE, base::Bind(callback, std::vector<std::string>(),
+                                    SERVICE_WORKER_ERROR_ABORT));
+    }
     return;
   }
+  DCHECK_EQ(INITIALIZED, state_);
 
   if (registration_id == kInvalidServiceWorkerRegistrationId || keys.empty()) {
     RunSoon(FROM_HERE, base::Bind(callback, std::vector<std::string>(),
@@ -688,7 +695,15 @@
 void ServiceWorkerStorage::ClearUserData(int64_t registration_id,
                                          const std::vector<std::string>& keys,
                                          const StatusCallback& callback) {
-  DCHECK(state_ == INITIALIZED || state_ == DISABLED) << state_;
+  if (!LazyInitialize(base::Bind(&ServiceWorkerStorage::ClearUserData,
+                                 weak_factory_.GetWeakPtr(), registration_id,
+                                 keys, callback))) {
+    if (state_ != INITIALIZING)
+      RunSoon(FROM_HERE, base::Bind(callback, SERVICE_WORKER_ERROR_ABORT));
+    return;
+  }
+  DCHECK_EQ(INITIALIZED, state_);
+
   if (IsDisabled()) {
     RunSoon(FROM_HERE, base::Bind(callback, SERVICE_WORKER_ERROR_ABORT));
     return;
diff --git a/content/browser/service_worker/service_worker_storage_unittest.cc b/content/browser/service_worker/service_worker_storage_unittest.cc
index 97312a5..fb743e9 100644
--- a/content/browser/service_worker/service_worker_storage_unittest.cc
+++ b/content/browser/service_worker/service_worker_storage_unittest.cc
@@ -1096,6 +1096,35 @@
             GetUserDataForAllRegistrations(std::string(), &data_list_out));
 }
 
+// The *_BeforeInitialize tests exercise the API before LazyInitialize() is
+// called.
+TEST_P(ServiceWorkerStorageTestP, StoreUserData_BeforeInitialize) {
+  const int kRegistrationId = 0;
+  EXPECT_EQ(SERVICE_WORKER_ERROR_NOT_FOUND,
+            StoreUserData(kRegistrationId, GURL("https://example.com"),
+                          {{"key", "data"}}));
+}
+
+TEST_P(ServiceWorkerStorageTestP, GetUserData_BeforeInitialize) {
+  const int kRegistrationId = 0;
+  std::vector<std::string> data_out;
+  EXPECT_EQ(SERVICE_WORKER_ERROR_NOT_FOUND,
+            GetUserData(kRegistrationId, {"key"}, &data_out));
+}
+
+TEST_P(ServiceWorkerStorageTestP, ClearUserData_BeforeInitialize) {
+  const int kRegistrationId = 0;
+  EXPECT_EQ(SERVICE_WORKER_OK, ClearUserData(kRegistrationId, {"key"}));
+}
+
+TEST_P(ServiceWorkerStorageTestP,
+       GetUserDataForAllRegistrations_BeforeInitialize) {
+  std::vector<std::pair<int64_t, std::string>> data_list_out;
+  EXPECT_EQ(SERVICE_WORKER_OK,
+            GetUserDataForAllRegistrations("key", &data_list_out));
+  EXPECT_TRUE(data_list_out.empty());
+}
+
 class ServiceWorkerResourceStorageTest : public ServiceWorkerStorageTestP {
  public:
   void SetUp() override {
diff --git a/content/browser/storage_partition_impl.cc b/content/browser/storage_partition_impl.cc
index 9d9b2a6..1562ad45 100644
--- a/content/browser/storage_partition_impl.cc
+++ b/content/browser/storage_partition_impl.cc
@@ -22,10 +22,12 @@
 #include "content/common/dom_storage/dom_storage_types.h"
 #include "content/public/browser/browser_context.h"
 #include "content/public/browser/browser_thread.h"
+#include "content/public/browser/content_browser_client.h"
 #include "content/public/browser/dom_storage_context.h"
 #include "content/public/browser/indexed_db_context.h"
 #include "content/public/browser/local_storage_usage_info.h"
 #include "content/public/browser/session_storage_usage_info.h"
+#include "content/public/common/content_client.h"
 #include "net/base/completion_callback.h"
 #include "net/base/net_errors.h"
 #include "net/cookies/canonical_cookie.h"
@@ -363,36 +365,11 @@
 StoragePartitionImpl::StoragePartitionImpl(
     BrowserContext* browser_context,
     const base::FilePath& partition_path,
-    storage::QuotaManager* quota_manager,
-    ChromeAppCacheService* appcache_service,
-    storage::FileSystemContext* filesystem_context,
-    storage::DatabaseTracker* database_tracker,
-    DOMStorageContextWrapper* dom_storage_context,
-    IndexedDBContextImpl* indexed_db_context,
-    CacheStorageContextImpl* cache_storage_context,
-    ServiceWorkerContextWrapper* service_worker_context,
-    storage::SpecialStoragePolicy* special_storage_policy,
-    HostZoomLevelContext* host_zoom_level_context,
-    PlatformNotificationContextImpl* platform_notification_context,
-    BackgroundSyncContext* background_sync_context,
-    PaymentAppContextImpl* payment_app_context,
-    scoped_refptr<BroadcastChannelProvider> broadcast_channel_provider)
+    storage::SpecialStoragePolicy* special_storage_policy)
     : partition_path_(partition_path),
-      quota_manager_(quota_manager),
-      appcache_service_(appcache_service),
-      filesystem_context_(filesystem_context),
-      database_tracker_(database_tracker),
-      dom_storage_context_(dom_storage_context),
-      indexed_db_context_(indexed_db_context),
-      cache_storage_context_(cache_storage_context),
-      service_worker_context_(service_worker_context),
       special_storage_policy_(special_storage_policy),
-      host_zoom_level_context_(host_zoom_level_context),
-      platform_notification_context_(platform_notification_context),
-      background_sync_context_(background_sync_context),
-      payment_app_context_(payment_app_context),
-      broadcast_channel_provider_(std::move(broadcast_channel_provider)),
-      browser_context_(browser_context) {}
+      browser_context_(browser_context),
+      weak_factory_(this) {}
 
 StoragePartitionImpl::~StoragePartitionImpl() {
   browser_context_ = nullptr;
@@ -440,34 +417,38 @@
   base::FilePath partition_path =
       context->GetPath().Append(relative_partition_path);
 
+  std::unique_ptr<StoragePartitionImpl> partition =
+      base::WrapUnique(new StoragePartitionImpl(
+          context, partition_path, context->GetSpecialStoragePolicy()));
+
   // All of the clients have to be created and registered with the
   // QuotaManager prior to the QuotaManger being used. We do them
   // all together here prior to handing out a reference to anything
   // that utilizes the QuotaManager.
-  scoped_refptr<storage::QuotaManager> quota_manager =
-      new storage::QuotaManager(
-          in_memory, partition_path,
-          BrowserThread::GetTaskRunnerForThread(BrowserThread::IO).get(),
-          BrowserThread::GetTaskRunnerForThread(BrowserThread::DB).get(),
-          context->GetSpecialStoragePolicy());
+  partition->quota_manager_ = new storage::QuotaManager(
+      in_memory, partition_path,
+      BrowserThread::GetTaskRunnerForThread(BrowserThread::IO).get(),
+      BrowserThread::GetTaskRunnerForThread(BrowserThread::DB).get(),
+      context->GetSpecialStoragePolicy(),
+      base::Bind(&StoragePartitionImpl::GetQuotaSettings,
+                 partition->weak_factory_.GetWeakPtr()));
+  scoped_refptr<storage::QuotaManagerProxy> quota_manager_proxy =
+      partition->quota_manager_->proxy();
 
   // Each consumer is responsible for registering its QuotaClient during
   // its construction.
-  scoped_refptr<storage::FileSystemContext> filesystem_context =
-      CreateFileSystemContext(
-          context, partition_path, in_memory, quota_manager->proxy());
+  partition->filesystem_context_ = CreateFileSystemContext(
+      context, partition_path, in_memory, quota_manager_proxy.get());
 
-  scoped_refptr<storage::DatabaseTracker> database_tracker =
-      new storage::DatabaseTracker(
-          partition_path, in_memory, context->GetSpecialStoragePolicy(),
-          quota_manager->proxy(),
-          BrowserThread::GetTaskRunnerForThread(BrowserThread::FILE).get());
+  partition->database_tracker_ = new storage::DatabaseTracker(
+      partition_path, in_memory, context->GetSpecialStoragePolicy(),
+      quota_manager_proxy.get(),
+      BrowserThread::GetTaskRunnerForThread(BrowserThread::FILE).get());
 
-  scoped_refptr<DOMStorageContextWrapper> dom_storage_context =
-      new DOMStorageContextWrapper(
-          BrowserContext::GetConnectorFor(context),
-          in_memory ? base::FilePath() : context->GetPath(),
-          relative_partition_path, context->GetSpecialStoragePolicy());
+  partition->dom_storage_context_ = new DOMStorageContextWrapper(
+      BrowserContext::GetConnectorFor(context),
+      in_memory ? base::FilePath() : context->GetPath(),
+      relative_partition_path, context->GetSpecialStoragePolicy());
 
   // BrowserMainLoop may not be initialized in unit tests. Tests will
   // need to inject their own task runner into the IndexedDBContext.
@@ -481,60 +462,38 @@
           : NULL;
 
   base::FilePath path = in_memory ? base::FilePath() : partition_path;
-  scoped_refptr<IndexedDBContextImpl> indexed_db_context =
-      new IndexedDBContextImpl(path,
-                               context->GetSpecialStoragePolicy(),
-                               quota_manager->proxy(),
-                               idb_task_runner);
+  partition->indexed_db_context_ =
+      new IndexedDBContextImpl(path, context->GetSpecialStoragePolicy(),
+                               quota_manager_proxy.get(), idb_task_runner);
 
-  scoped_refptr<CacheStorageContextImpl> cache_storage_context =
-      new CacheStorageContextImpl(context);
-  cache_storage_context->Init(path, make_scoped_refptr(quota_manager->proxy()));
+  partition->cache_storage_context_ = new CacheStorageContextImpl(context);
+  partition->cache_storage_context_->Init(path, quota_manager_proxy);
 
-  scoped_refptr<ServiceWorkerContextWrapper> service_worker_context =
-      new ServiceWorkerContextWrapper(context);
-  service_worker_context->Init(path, quota_manager->proxy(),
-                               context->GetSpecialStoragePolicy());
+  partition->service_worker_context_ = new ServiceWorkerContextWrapper(context);
+  partition->service_worker_context_->Init(path, quota_manager_proxy.get(),
+                                           context->GetSpecialStoragePolicy());
+  partition->service_worker_context_->set_storage_partition(partition.get());
 
-  scoped_refptr<ChromeAppCacheService> appcache_service =
-      new ChromeAppCacheService(quota_manager->proxy());
+  partition->appcache_service_ =
+      new ChromeAppCacheService(quota_manager_proxy.get());
 
-  scoped_refptr<storage::SpecialStoragePolicy> special_storage_policy(
-      context->GetSpecialStoragePolicy());
+  partition->host_zoom_level_context_ = new HostZoomLevelContext(
+      context->CreateZoomLevelDelegate(partition_path));
 
-  scoped_refptr<HostZoomLevelContext> host_zoom_level_context(
-      new HostZoomLevelContext(
-          context->CreateZoomLevelDelegate(partition_path)));
-
-  scoped_refptr<PlatformNotificationContextImpl> platform_notification_context =
+  partition->platform_notification_context_ =
       new PlatformNotificationContextImpl(path, context,
-                                          service_worker_context);
-  platform_notification_context->Initialize();
+                                          partition->service_worker_context_);
+  partition->platform_notification_context_->Initialize();
 
-  scoped_refptr<BackgroundSyncContext> background_sync_context =
-      new BackgroundSyncContext();
-  background_sync_context->Init(service_worker_context);
+  partition->background_sync_context_ = new BackgroundSyncContext();
+  partition->background_sync_context_->Init(partition->service_worker_context_);
 
-  scoped_refptr<PaymentAppContextImpl> payment_app_context =
-      new PaymentAppContextImpl(service_worker_context);
+  partition->payment_app_context_ = new PaymentAppContextImpl(
+      partition->service_worker_context_);
 
-  scoped_refptr<BroadcastChannelProvider>
-      broadcast_channel_provider = new BroadcastChannelProvider();
+  partition->broadcast_channel_provider_ = new BroadcastChannelProvider();
 
-  std::unique_ptr<StoragePartitionImpl> storage_partition(
-      new StoragePartitionImpl(
-          context, partition_path, quota_manager.get(), appcache_service.get(),
-          filesystem_context.get(), database_tracker.get(),
-          dom_storage_context.get(), indexed_db_context.get(),
-          cache_storage_context.get(), service_worker_context.get(),
-          special_storage_policy.get(), host_zoom_level_context.get(),
-          platform_notification_context.get(), background_sync_context.get(),
-          payment_app_context.get(),
-          std::move(broadcast_channel_provider)));
-
-  service_worker_context->set_storage_partition(storage_partition.get());
-
-  return storage_partition;
+  return partition;
 }
 
 base::FilePath StoragePartitionImpl::GetPath() {
@@ -934,4 +893,10 @@
   media_url_request_context_ = media_url_request_context;
 }
 
+void StoragePartitionImpl::GetQuotaSettings(
+    const storage::OptionalQuotaSettingsCallback& callback) {
+  GetContentClient()->browser()->GetQuotaSettings(browser_context_, this,
+                                                  callback);
+}
+
 }  // namespace content
diff --git a/content/browser/storage_partition_impl.h b/content/browser/storage_partition_impl.h
index f00d02f..e3822dab 100644
--- a/content/browser/storage_partition_impl.h
+++ b/content/browser/storage_partition_impl.h
@@ -14,6 +14,7 @@
 #include "base/gtest_prod_util.h"
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
+#include "base/memory/weak_ptr.h"
 #include "content/browser/appcache/chrome_appcache_service.h"
 #include "content/browser/background_sync/background_sync_context.h"
 #include "content/browser/broadcast_channel/broadcast_channel_provider.h"
@@ -165,23 +166,9 @@
       bool in_memory,
       const base::FilePath& relative_partition_path);
 
-  StoragePartitionImpl(
-      BrowserContext* browser_context,
-      const base::FilePath& partition_path,
-      storage::QuotaManager* quota_manager,
-      ChromeAppCacheService* appcache_service,
-      storage::FileSystemContext* filesystem_context,
-      storage::DatabaseTracker* database_tracker,
-      DOMStorageContextWrapper* dom_storage_context,
-      IndexedDBContextImpl* indexed_db_context,
-      CacheStorageContextImpl* cache_storage_context,
-      ServiceWorkerContextWrapper* service_worker_context,
-      storage::SpecialStoragePolicy* special_storage_policy,
-      HostZoomLevelContext* host_zoom_level_context,
-      PlatformNotificationContextImpl* platform_notification_context,
-      BackgroundSyncContext* background_sync_context,
-      PaymentAppContextImpl* payment_app_context,
-      scoped_refptr<BroadcastChannelProvider>broadcast_channel_provider);
+  StoragePartitionImpl(BrowserContext* browser_context,
+                       const base::FilePath& partition_path,
+                       storage::SpecialStoragePolicy* special_storage_policy);
 
   // We will never have both remove_origin be populated and a cookie_matcher.
   void ClearDataImpl(uint32_t remove_mask,
@@ -211,6 +198,10 @@
   void SetMediaURLRequestContext(
       net::URLRequestContextGetter* media_url_request_context);
 
+  // Function used by the quota system to ask the embedder for the
+  // storage configuration info.
+  void GetQuotaSettings(const storage::OptionalQuotaSettingsCallback& callback);
+
   base::FilePath partition_path_;
   scoped_refptr<net::URLRequestContextGetter> url_request_context_;
   scoped_refptr<net::URLRequestContextGetter> media_url_request_context_;
@@ -236,6 +227,8 @@
   // BrowserContext is destroyed, |this| will be destroyed too.
   BrowserContext* browser_context_;
 
+  base::WeakPtrFactory<StoragePartitionImpl> weak_factory_;
+
   DISALLOW_COPY_AND_ASSIGN(StoragePartitionImpl);
 };
 
diff --git a/content/browser/storage_partition_impl_map.cc b/content/browser/storage_partition_impl_map.cc
index 0e85d8a6..3e65c39d 100644
--- a/content/browser/storage_partition_impl_map.cc
+++ b/content/browser/storage_partition_impl_map.cc
@@ -399,10 +399,6 @@
   StoragePartitionImpl* partition = partition_ptr.get();
   partitions_[partition_config] = std::move(partition_ptr);
 
-  partition->GetQuotaManager()->SetTemporaryStorageEvictionPolicy(
-      GetContentClient()->browser()->GetTemporaryStorageEvictionPolicy(
-          browser_context_));
-
   ChromeBlobStorageContext* blob_storage_context =
       ChromeBlobStorageContext::GetFor(browser_context_);
   StreamContext* stream_context = StreamContext::GetFor(browser_context_);
diff --git a/content/child/resource_dispatcher.cc b/content/child/resource_dispatcher.cc
index 89a604d..bdabb9f8 100644
--- a/content/child/resource_dispatcher.cc
+++ b/content/child/resource_dispatcher.cc
@@ -95,7 +95,7 @@
       mojom::DownloadedTempFilePtr downloaded_file) override {
     has_received_response_ = true;
     if (body_consumer_)
-      body_consumer_->Start(task_runner_.get());
+      body_consumer_->Start();
     downloaded_file_ = std::move(downloaded_file);
     resource_dispatcher_->OnMessageReceived(
         ResourceMsg_ReceivedResponse(request_id_, response_head));
@@ -120,7 +120,7 @@
     body_consumer_ = new URLResponseBodyConsumer(
         request_id_, resource_dispatcher_, std::move(body), task_runner_);
     if (has_received_response_)
-      body_consumer_->Start(task_runner_.get());
+      body_consumer_->Start();
   }
 
   void OnComplete(const ResourceRequestCompletionStatus& status) override {
diff --git a/content/child/url_response_body_consumer.cc b/content/child/url_response_body_consumer.cc
index 101aee6..9d5c017 100644
--- a/content/child/url_response_body_consumer.cc
+++ b/content/child/url_response_body_consumer.cc
@@ -4,6 +4,7 @@
 
 #include "content/child/url_response_body_consumer.h"
 
+#include "base/auto_reset.h"
 #include "base/bind.h"
 #include "base/macros.h"
 #include "base/memory/ptr_util.h"
@@ -45,17 +46,18 @@
       resource_dispatcher_(resource_dispatcher),
       handle_(std::move(handle)),
       handle_watcher_(task_runner),
+      task_runner_(task_runner),
       has_seen_end_of_data_(!handle_.is_valid()) {}
 
 URLResponseBodyConsumer::~URLResponseBodyConsumer() {}
 
-void URLResponseBodyConsumer::Start(base::SingleThreadTaskRunner* task_runner) {
+void URLResponseBodyConsumer::Start() {
   if (has_been_cancelled_)
     return;
   handle_watcher_.Start(
       handle_.get(), MOJO_HANDLE_SIGNAL_READABLE,
       base::Bind(&URLResponseBodyConsumer::OnReadable, base::Unretained(this)));
-  task_runner->PostTask(
+  task_runner_->PostTask(
       FROM_HERE, base::Bind(&URLResponseBodyConsumer::OnReadable, AsWeakPtr(),
                             MOJO_RESULT_OK));
 }
@@ -77,14 +79,24 @@
 void URLResponseBodyConsumer::Reclaim(uint32_t size) {
   MojoResult result = mojo::EndReadDataRaw(handle_.get(), size);
   DCHECK_EQ(MOJO_RESULT_OK, result);
+
+  if (is_in_on_readable_)
+    return;
+
+  task_runner_->PostTask(
+      FROM_HERE, base::Bind(&URLResponseBodyConsumer::OnReadable, AsWeakPtr(),
+                            MOJO_RESULT_OK));
 }
 
 void URLResponseBodyConsumer::OnReadable(MojoResult unused) {
+  DCHECK(!is_in_on_readable_);
+
   if (has_been_cancelled_ || has_seen_end_of_data_)
     return;
 
   // Protect |this| as RequestPeer::OnReceivedData may call deref.
   scoped_refptr<URLResponseBodyConsumer> protect(this);
+  base::AutoReset<bool> is_in_on_readable(&is_in_on_readable_, true);
 
   // TODO(yhirano): Suppress notification when deferred.
   while (!has_been_cancelled_) {
@@ -92,7 +104,7 @@
     uint32_t available = 0;
     MojoResult result = mojo::BeginReadDataRaw(
         handle_.get(), &buffer, &available, MOJO_READ_DATA_FLAG_NONE);
-    if (result == MOJO_RESULT_SHOULD_WAIT)
+    if (result == MOJO_RESULT_SHOULD_WAIT || result == MOJO_RESULT_BUSY)
       return;
     if (result == MOJO_RESULT_FAILED_PRECONDITION) {
       has_seen_end_of_data_ = true;
diff --git a/content/child/url_response_body_consumer.h b/content/child/url_response_body_consumer.h
index 13e122d2..09a988bdd 100644
--- a/content/child/url_response_body_consumer.h
+++ b/content/child/url_response_body_consumer.h
@@ -38,7 +38,7 @@
       scoped_refptr<base::SingleThreadTaskRunner> task_runner);
 
   // Starts watching the handle.
-  void Start(base::SingleThreadTaskRunner* task_runner);
+  void Start();
 
   // Sets the completion status. The completion status is dispatched to the
   // ResourceDispatcher when the both following conditions hold:
@@ -66,10 +66,12 @@
   mojo::ScopedDataPipeConsumerHandle handle_;
   mojo::Watcher handle_watcher_;
   ResourceRequestCompletionStatus completion_status_;
+  scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
 
   bool has_received_completion_ = false;
   bool has_been_cancelled_ = false;
   bool has_seen_end_of_data_;
+  bool is_in_on_readable_ = false;
 
   DISALLOW_COPY_AND_ASSIGN(URLResponseBodyConsumer);
 };
diff --git a/content/child/url_response_body_consumer_unittest.cc b/content/child/url_response_body_consumer_unittest.cc
index 8bf435c..3496b4e4 100644
--- a/content/child/url_response_body_consumer_unittest.cc
+++ b/content/child/url_response_body_consumer_unittest.cc
@@ -29,7 +29,9 @@
 class TestRequestPeer : public RequestPeer {
  public:
   struct Context;
-  explicit TestRequestPeer(Context* context) : context_(context) {}
+  TestRequestPeer(Context* context,
+                  scoped_refptr<base::SingleThreadTaskRunner> task_runner)
+      : context_(context), task_runner_(std::move(task_runner)) {}
 
   void OnUploadProgress(uint64_t position, uint64_t size) override {
     ADD_FAILURE() << "OnUploadProgress should not be called.";
@@ -52,6 +54,8 @@
   void OnReceivedData(std::unique_ptr<ReceivedData> data) override {
     EXPECT_FALSE(context_->complete);
     context_->data.append(data->payload(), data->length());
+    if (context_->release_data_asynchronously)
+      task_runner_->DeleteSoon(FROM_HERE, data.release());
     context_->run_loop_quit_closure.Run();
   }
 
@@ -75,10 +79,12 @@
     bool complete = false;
     base::Closure run_loop_quit_closure;
     int error_code = net::OK;
+    bool release_data_asynchronously = false;
   };
 
  private:
   Context* context_;
+  scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
 
   DISALLOW_COPY_AND_ASSIGN(TestRequestPeer);
 };
@@ -130,7 +136,7 @@
                        TestRequestPeer::Context* context) {
     return dispatcher_->StartAsync(
         std::move(request), 0, nullptr, url::Origin(),
-        base::MakeUnique<TestRequestPeer>(context),
+        base::MakeUnique<TestRequestPeer>(context, message_loop_.task_runner()),
         blink::WebURLRequest::LoadingIPCType::ChromeIPC, nullptr, nullptr);
   }
 
@@ -154,7 +160,7 @@
   scoped_refptr<URLResponseBodyConsumer> consumer(new URLResponseBodyConsumer(
       request_id, dispatcher_.get(), std::move(data_pipe.consumer_handle),
       message_loop_.task_runner()));
-  consumer->Start(message_loop_.task_runner().get());
+  consumer->Start();
 
   mojo::ScopedDataPipeProducerHandle writer =
       std::move(data_pipe.producer_handle);
@@ -180,7 +186,43 @@
   scoped_refptr<URLResponseBodyConsumer> consumer(new URLResponseBodyConsumer(
       request_id, dispatcher_.get(), std::move(data_pipe.consumer_handle),
       message_loop_.task_runner()));
-  consumer->Start(message_loop_.task_runner().get());
+  consumer->Start();
+
+  consumer->OnComplete(ResourceRequestCompletionStatus());
+  mojo::ScopedDataPipeProducerHandle writer =
+      std::move(data_pipe.producer_handle);
+  std::string buffer = "hello";
+  uint32_t size = buffer.size();
+  MojoResult result =
+      mojo::WriteDataRaw(writer.get(), buffer.c_str(), &size, kNone);
+  ASSERT_EQ(MOJO_RESULT_OK, result);
+  ASSERT_EQ(buffer.size(), size);
+
+  Run(&context);
+
+  writer.reset();
+  EXPECT_FALSE(context.complete);
+  EXPECT_EQ("hello", context.data);
+
+  Run(&context);
+
+  EXPECT_TRUE(context.complete);
+  EXPECT_EQ("hello", context.data);
+}
+
+// Release the received data asynchronously. This leads to MOJO_RESULT_BUSY
+// from the BeginReadDataRaw call in OnReadable.
+TEST_F(URLResponseBodyConsumerTest, OnCompleteThenCloseWithAsyncRelease) {
+  TestRequestPeer::Context context;
+  context.release_data_asynchronously = true;
+  std::unique_ptr<ResourceRequest> request(CreateResourceRequest());
+  int request_id = SetUpRequestPeer(std::move(request), &context);
+  mojo::DataPipe data_pipe(CreateDataPipeOptions());
+
+  scoped_refptr<URLResponseBodyConsumer> consumer(new URLResponseBodyConsumer(
+      request_id, dispatcher_.get(), std::move(data_pipe.consumer_handle),
+      message_loop_.task_runner()));
+  consumer->Start();
 
   consumer->OnComplete(ResourceRequestCompletionStatus());
   mojo::ScopedDataPipeProducerHandle writer =
@@ -213,7 +255,7 @@
   scoped_refptr<URLResponseBodyConsumer> consumer(new URLResponseBodyConsumer(
       request_id, dispatcher_.get(), std::move(data_pipe.consumer_handle),
       message_loop_.task_runner()));
-  consumer->Start(message_loop_.task_runner().get());
+  consumer->Start();
 
   ResourceRequestCompletionStatus status;
   status.error_code = net::ERR_FAILED;
diff --git a/content/public/browser/BUILD.gn b/content/public/browser/BUILD.gn
index 18ad2f7..021e8f5 100644
--- a/content/public/browser/BUILD.gn
+++ b/content/public/browser/BUILD.gn
@@ -299,6 +299,9 @@
 
     # We expose skia headers in the public API.
     "//skia",
+
+    # We expose storage headers for quota in the public API.
+    "//storage/browser",
     "//third_party/WebKit/public:mojo_bindings",
   ]
   deps = [
@@ -311,7 +314,6 @@
     "//media",
     "//net",
     "//ppapi/c",
-    "//storage/browser",
     "//ui/accessibility",
     "//ui/base",
     "//ui/events",
diff --git a/content/public/browser/content_browser_client.cc b/content/public/browser/content_browser_client.cc
index f6c8cdb..6d8fb21d 100644
--- a/content/public/browser/content_browser_client.cc
+++ b/content/public/browser/content_browser_client.cc
@@ -6,6 +6,7 @@
 
 #include "base/files/file_path.h"
 #include "base/guid.h"
+#include "base/logging.h"
 #include "build/build_config.h"
 #include "content/public/browser/client_certificate_delegate.h"
 #include "content/public/browser/memory_coordinator_delegate.h"
@@ -232,10 +233,12 @@
   return nullptr;
 }
 
-std::unique_ptr<storage::QuotaEvictionPolicy>
-ContentBrowserClient::GetTemporaryStorageEvictionPolicy(
-    content::BrowserContext* context) {
-  return std::unique_ptr<storage::QuotaEvictionPolicy>();
+void ContentBrowserClient::GetQuotaSettings(
+    BrowserContext* context,
+    StoragePartition* partition,
+    const storage::OptionalQuotaSettingsCallback& callback) {
+  // By default, no quota is provided, embedders should override.
+  callback.Run(storage::GetNoQuotaSettings());
 }
 
 void ContentBrowserClient::SelectClientCertificate(
diff --git a/content/public/browser/content_browser_client.h b/content/public/browser/content_browser_client.h
index 39316c3..437ca897 100644
--- a/content/public/browser/content_browser_client.h
+++ b/content/public/browser/content_browser_client.h
@@ -32,6 +32,7 @@
 #include "net/base/mime_util.h"
 #include "net/cookies/canonical_cookie.h"
 #include "storage/browser/fileapi/file_system_context.h"
+#include "storage/browser/quota/quota_manager.h"
 #include "third_party/WebKit/public/platform/WebPageVisibilityState.h"
 #include "ui/base/page_transition_types.h"
 #include "ui/base/window_open_disposition.h"
@@ -100,7 +101,6 @@
 
 namespace storage {
 class FileSystemBackend;
-class QuotaEvictionPolicy;
 }
 
 namespace content {
@@ -127,6 +127,7 @@
 class ResourceContext;
 class SiteInstance;
 class SpeechRecognitionManagerDelegate;
+class StoragePartition;
 class TracingDelegate;
 class VpnServiceProxy;
 class WebContents;
@@ -462,10 +463,13 @@
   // Create and return a new quota permission context.
   virtual QuotaPermissionContext* CreateQuotaPermissionContext();
 
-  // Gives the embedder a chance to register a custom QuotaEvictionPolicy for
-  // temporary storage.
-  virtual std::unique_ptr<storage::QuotaEvictionPolicy>
-  GetTemporaryStorageEvictionPolicy(BrowserContext* context);
+  // Allows the embedder to provide settings that determine the amount
+  // of disk space that may be used by content facing storage apis like
+  // IndexedDatabase and ServiceWorker::CacheStorage and others.
+  virtual void GetQuotaSettings(
+      content::BrowserContext* context,
+      content::StoragePartition* partition,
+      const storage::OptionalQuotaSettingsCallback& callback);
 
   // Informs the embedder that a certificate error has occured.  If
   // |overridable| is true and if |strict_enforcement| is false, the user
diff --git a/content/public/test/mock_special_storage_policy.cc b/content/public/test/mock_special_storage_policy.cc
index f3b3c02..0226b98e 100644
--- a/content/public/test/mock_special_storage_policy.cc
+++ b/content/public/test/mock_special_storage_policy.cc
@@ -26,10 +26,6 @@
   return base::ContainsKey(session_only_, origin);
 }
 
-bool MockSpecialStoragePolicy::CanQueryDiskSize(const GURL& origin) {
-  return base::ContainsKey(can_query_disk_size_, origin);
-}
-
 bool MockSpecialStoragePolicy::HasIsolatedStorage(const GURL& origin) {
   return base::ContainsKey(isolated_, origin);
 }
diff --git a/content/public/test/mock_special_storage_policy.h b/content/public/test/mock_special_storage_policy.h
index 96003e8c..b4a2a6c 100644
--- a/content/public/test/mock_special_storage_policy.h
+++ b/content/public/test/mock_special_storage_policy.h
@@ -22,7 +22,6 @@
   bool IsStorageProtected(const GURL& origin) override;
   bool IsStorageUnlimited(const GURL& origin) override;
   bool IsStorageSessionOnly(const GURL& origin) override;
-  bool CanQueryDiskSize(const GURL& origin) override;
   bool HasIsolatedStorage(const GURL& origin) override;
   bool HasSessionOnlyOrigins() override;
   bool IsStorageDurable(const GURL& origin) override;
@@ -43,10 +42,6 @@
     session_only_.insert(origin);
   }
 
-  void GrantQueryDiskSize(const GURL& origin) {
-    can_query_disk_size_.insert(origin);
-  }
-
   void AddIsolated(const GURL& origin) {
     isolated_.insert(origin);
   }
@@ -67,7 +62,6 @@
     protected_.clear();
     unlimited_.clear();
     session_only_.clear();
-    can_query_disk_size_.clear();
     file_handlers_.clear();
     isolated_.clear();
     all_unlimited_ = false;
@@ -92,7 +86,6 @@
   std::set<GURL> protected_;
   std::set<GURL> unlimited_;
   std::set<GURL> session_only_;
-  std::set<GURL> can_query_disk_size_;
   std::set<GURL> isolated_;
   std::set<GURL> durable_;
   std::set<std::string> file_handlers_;
diff --git a/content/shell/browser/layout_test/layout_test_browser_main_parts.cc b/content/shell/browser/layout_test/layout_test_browser_main_parts.cc
index acb5e2a4..7f01424a 100644
--- a/content/shell/browser/layout_test/layout_test_browser_main_parts.cc
+++ b/content/shell/browser/layout_test/layout_test_browser_main_parts.cc
@@ -27,7 +27,6 @@
 #include "net/base/net_module.h"
 #include "net/grit/net_resources.h"
 #include "ppapi/features/features.h"
-#include "storage/browser/quota/quota_manager.h"
 #include "ui/base/resource/resource_bundle.h"
 #include "url/gurl.h"
 
@@ -51,13 +50,6 @@
 
 namespace content {
 
-namespace {
-
-// Default quota for each origin is 5MB.
-const int kDefaultLayoutTestQuotaBytes = 5 * 1024 * 1024;
-
-}  // namespace
-
 LayoutTestBrowserMainParts::LayoutTestBrowserMainParts(
     const MainFunctionParams& parameters)
     : ShellBrowserMainParts(parameters) {
@@ -72,18 +64,6 @@
 }
 
 void LayoutTestBrowserMainParts::InitializeMessageLoopContext() {
-  storage::QuotaManager* quota_manager =
-      BrowserContext::GetDefaultStoragePartition(browser_context())
-          ->GetQuotaManager();
-  BrowserThread::PostTask(
-      BrowserThread::IO,
-      FROM_HERE,
-      base::Bind(&storage::QuotaManager::SetTemporaryGlobalOverrideQuota,
-                 quota_manager,
-                 kDefaultLayoutTestQuotaBytes *
-                     storage::QuotaManager::kPerHostTemporaryPortion,
-                 storage::QuotaCallback()));
-
 #if BUILDFLAG(ENABLE_PLUGINS)
   PluginService* plugin_service = PluginService::GetInstance();
   plugin_service_filter_.reset(new ShellPluginServiceFilter);
diff --git a/content/shell/browser/layout_test/layout_test_content_browser_client.cc b/content/shell/browser/layout_test/layout_test_content_browser_client.cc
index 3b25ecd..f913fdb 100644
--- a/content/shell/browser/layout_test/layout_test_content_browser_client.cc
+++ b/content/shell/browser/layout_test/layout_test_content_browser_client.cc
@@ -128,6 +128,13 @@
   return shell_browser_main_parts();
 }
 
+void LayoutTestContentBrowserClient::GetQuotaSettings(
+    BrowserContext* context,
+    StoragePartition* partition,
+    const storage::OptionalQuotaSettingsCallback& callback) {
+  callback.Run(storage::GetHardCodedSettings(5 * 1024 * 1024));
+}
+
 PlatformNotificationService*
 LayoutTestContentBrowserClient::GetPlatformNotificationService() {
   return layout_test_notification_manager_.get();
diff --git a/content/shell/browser/layout_test/layout_test_content_browser_client.h b/content/shell/browser/layout_test/layout_test_content_browser_client.h
index e3fe22f..f2091ea 100644
--- a/content/shell/browser/layout_test/layout_test_content_browser_client.h
+++ b/content/shell/browser/layout_test/layout_test_content_browser_client.h
@@ -37,6 +37,10 @@
                                       int child_process_id) override;
   BrowserMainParts* CreateBrowserMainParts(
       const MainFunctionParams& parameters) override;
+  void GetQuotaSettings(
+      content::BrowserContext* context,
+      content::StoragePartition* partition,
+      const storage::OptionalQuotaSettingsCallback& callback) override;
 
   PlatformNotificationService* GetPlatformNotificationService() override;
 
diff --git a/content/shell/browser/layout_test/layout_test_message_filter.cc b/content/shell/browser/layout_test/layout_test_message_filter.cc
index 248f3e1..fe7ddf46 100644
--- a/content/shell/browser/layout_test/layout_test_message_filter.cc
+++ b/content/shell/browser/layout_test/layout_test_message_filter.cc
@@ -119,9 +119,7 @@
 }
 
 void LayoutTestMessageFilter::OnSetDatabaseQuota(int quota) {
-  quota_manager_->SetTemporaryGlobalOverrideQuota(
-      quota * storage::QuotaManager::kPerHostTemporaryPortion,
-      storage::QuotaCallback());
+  quota_manager_->SetQuotaSettings(storage::GetHardCodedSettings(quota));
 }
 
 void LayoutTestMessageFilter::OnSimulateWebNotificationClick(
diff --git a/content/shell/browser/shell_content_browser_client.cc b/content/shell/browser/shell_content_browser_client.cc
index 2b44625..cb70799 100644
--- a/content/shell/browser/shell_content_browser_client.cc
+++ b/content/shell/browser/shell_content_browser_client.cc
@@ -39,6 +39,7 @@
 #include "content/shell/common/shell_switches.h"
 #include "grit/shell_resources.h"
 #include "net/url_request/url_request_context_getter.h"
+#include "storage/browser/quota/quota_settings.h"
 #include "ui/base/resource/resource_bundle.h"
 #include "url/gurl.h"
 #include "url/origin.h"
@@ -292,6 +293,13 @@
   return new ShellQuotaPermissionContext();
 }
 
+void ShellContentBrowserClient::GetQuotaSettings(
+    BrowserContext* context,
+    StoragePartition* partition,
+    const storage::OptionalQuotaSettingsCallback& callback) {
+  callback.Run(storage::GetHardCodedSettings(100 * 1024 * 1024));
+}
+
 void ShellContentBrowserClient::SelectClientCertificate(
     WebContents* web_contents,
     net::SSLCertRequestInfo* cert_request_info,
diff --git a/content/shell/browser/shell_content_browser_client.h b/content/shell/browser/shell_content_browser_client.h
index d1937bfb..84365ef 100644
--- a/content/shell/browser/shell_content_browser_client.h
+++ b/content/shell/browser/shell_content_browser_client.h
@@ -47,6 +47,10 @@
   WebContentsViewDelegate* GetWebContentsViewDelegate(
       WebContents* web_contents) override;
   QuotaPermissionContext* CreateQuotaPermissionContext() override;
+  void GetQuotaSettings(
+      content::BrowserContext* context,
+      content::StoragePartition* partition,
+      const storage::OptionalQuotaSettingsCallback& callback) override;
   void SelectClientCertificate(
       WebContents* web_contents,
       net::SSLCertRequestInfo* cert_request_info,
diff --git a/content/test/test_content_browser_client.cc b/content/test/test_content_browser_client.cc
index b766e537..52a7ba6 100644
--- a/content/test/test_content_browser_client.cc
+++ b/content/test/test_content_browser_client.cc
@@ -6,6 +6,7 @@
 
 #include "base/files/file_path.h"
 #include "base/logging.h"
+#include "storage/browser/quota/quota_settings.h"
 
 namespace content {
 
@@ -23,4 +24,11 @@
   return download_dir_.GetPath();
 }
 
+void TestContentBrowserClient::GetQuotaSettings(
+    BrowserContext* context,
+    StoragePartition* partition,
+    const storage::OptionalQuotaSettingsCallback& callback) {
+  callback.Run(storage::GetHardCodedSettings(100 * 1024 * 1024));
+}
+
 }  // namespace content
diff --git a/content/test/test_content_browser_client.h b/content/test/test_content_browser_client.h
index 5945806..6511df27 100644
--- a/content/test/test_content_browser_client.h
+++ b/content/test/test_content_browser_client.h
@@ -20,6 +20,10 @@
   TestContentBrowserClient();
   ~TestContentBrowserClient() override;
   base::FilePath GetDefaultDownloadDirectory() override;
+  void GetQuotaSettings(
+      content::BrowserContext* context,
+      content::StoragePartition* partition,
+      const storage::OptionalQuotaSettingsCallback& callback) override;
 
  private:
   // Temporary directory for GetDefaultDownloadDirectory.
diff --git a/extensions/shell/browser/shell_content_browser_client.cc b/extensions/shell/browser/shell_content_browser_client.cc
index e9ee2ab..584f29c7 100644
--- a/extensions/shell/browser/shell_content_browser_client.cc
+++ b/extensions/shell/browser/shell_content_browser_client.cc
@@ -37,6 +37,7 @@
 #include "extensions/shell/browser/shell_extension_system.h"
 #include "extensions/shell/browser/shell_navigation_ui_data.h"
 #include "extensions/shell/browser/shell_speech_recognition_manager_delegate.h"
+#include "storage/browser/quota/quota_settings.h"
 #include "url/gurl.h"
 
 #if !defined(DISABLE_NACL)
@@ -120,6 +121,17 @@
   return true;
 }
 
+void ShellContentBrowserClient::GetQuotaSettings(
+    content::BrowserContext* context,
+    content::StoragePartition* partition,
+    const storage::OptionalQuotaSettingsCallback& callback) {
+  content::BrowserThread::PostTaskAndReplyWithResult(
+      content::BrowserThread::FILE, FROM_HERE,
+      base::Bind(&storage::CalculateNominalDynamicSettings,
+                 partition->GetPath(), context->IsOffTheRecord()),
+      callback);
+}
+
 bool ShellContentBrowserClient::IsHandledURL(const GURL& url) {
   if (!url.is_valid())
     return false;
diff --git a/extensions/shell/browser/shell_content_browser_client.h b/extensions/shell/browser/shell_content_browser_client.h
index f3621dc..d626afa 100644
--- a/extensions/shell/browser/shell_content_browser_client.h
+++ b/extensions/shell/browser/shell_content_browser_client.h
@@ -43,7 +43,10 @@
   void RenderProcessWillLaunch(content::RenderProcessHost* host) override;
   bool ShouldUseProcessPerSite(content::BrowserContext* browser_context,
                                const GURL& effective_url) override;
-  // TODO(jamescook): Quota management?
+  void GetQuotaSettings(
+      content::BrowserContext* context,
+      content::StoragePartition* partition,
+      const storage::OptionalQuotaSettingsCallback& callback) override;
   bool IsHandledURL(const GURL& url) override;
   void SiteInstanceGotProcess(content::SiteInstance* site_instance) override;
   void SiteInstanceDeleting(content::SiteInstance* site_instance) override;
diff --git a/extensions/shell/browser/shell_special_storage_policy.cc b/extensions/shell/browser/shell_special_storage_policy.cc
index d43c71b..26b9dc2 100644
--- a/extensions/shell/browser/shell_special_storage_policy.cc
+++ b/extensions/shell/browser/shell_special_storage_policy.cc
@@ -30,10 +30,6 @@
   return false;
 }
 
-bool ShellSpecialStoragePolicy::CanQueryDiskSize(const GURL& origin) {
-  return true;
-}
-
 bool ShellSpecialStoragePolicy::HasSessionOnlyOrigins() {
   return false;
 }
diff --git a/extensions/shell/browser/shell_special_storage_policy.h b/extensions/shell/browser/shell_special_storage_policy.h
index dc78154..0973c3a 100644
--- a/extensions/shell/browser/shell_special_storage_policy.h
+++ b/extensions/shell/browser/shell_special_storage_policy.h
@@ -20,7 +20,6 @@
   bool IsStorageUnlimited(const GURL& origin) override;
   bool IsStorageDurable(const GURL& origin) override;
   bool IsStorageSessionOnly(const GURL& origin) override;
-  bool CanQueryDiskSize(const GURL& origin) override;
   bool HasIsolatedStorage(const GURL& origin) override;
   bool HasSessionOnlyOrigins() override;
 
diff --git a/headless/lib/browser/DEPS b/headless/lib/browser/DEPS
index 1704e22e..0df32e1 100644
--- a/headless/lib/browser/DEPS
+++ b/headless/lib/browser/DEPS
@@ -1,4 +1,5 @@
 include_rules = [
   "+components/security_state",
+  "+storage/browser/quota",
   "+ui/aura",
 ]
diff --git a/headless/lib/browser/headless_content_browser_client.cc b/headless/lib/browser/headless_content_browser_client.cc
index 1bfe88ff..e81f084 100644
--- a/headless/lib/browser/headless_content_browser_client.cc
+++ b/headless/lib/browser/headless_content_browser_client.cc
@@ -14,12 +14,14 @@
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/render_process_host.h"
 #include "content/public/browser/render_view_host.h"
+#include "content/public/browser/storage_partition.h"
 #include "content/public/common/service_names.mojom.h"
 #include "headless/grit/headless_lib_resources.h"
 #include "headless/lib/browser/headless_browser_context_impl.h"
 #include "headless/lib/browser/headless_browser_impl.h"
 #include "headless/lib/browser/headless_browser_main_parts.h"
 #include "headless/lib/browser/headless_devtools_manager_delegate.h"
+#include "storage/browser/quota/quota_settings.h"
 #include "ui/base/resource/resource_bundle.h"
 
 namespace headless {
@@ -86,4 +88,15 @@
   return manifest;
 }
 
+void HeadlessContentBrowserClient::GetQuotaSettings(
+    content::BrowserContext* context,
+    content::StoragePartition* partition,
+    const storage::OptionalQuotaSettingsCallback& callback) {
+  content::BrowserThread::PostTaskAndReplyWithResult(
+      content::BrowserThread::FILE, FROM_HERE,
+      base::Bind(&storage::CalculateNominalDynamicSettings,
+                 partition->GetPath(), context->IsOffTheRecord()),
+      callback);
+}
+
 }  // namespace headless
diff --git a/headless/lib/browser/headless_content_browser_client.h b/headless/lib/browser/headless_content_browser_client.h
index 511083bf..82c5d9e7 100644
--- a/headless/lib/browser/headless_content_browser_client.h
+++ b/headless/lib/browser/headless_content_browser_client.h
@@ -24,6 +24,10 @@
   content::DevToolsManagerDelegate* GetDevToolsManagerDelegate() override;
   std::unique_ptr<base::Value> GetServiceManifestOverlay(
       const std::string& name) override;
+  void GetQuotaSettings(
+      content::BrowserContext* context,
+      content::StoragePartition* partition,
+      const storage::OptionalQuotaSettingsCallback& callback) override;
 
  private:
   HeadlessBrowserImpl* browser_;  // Not owned.
diff --git a/media/blink/webmediaplayer_impl.cc b/media/blink/webmediaplayer_impl.cc
index 03b11fb..79c6966 100644
--- a/media/blink/webmediaplayer_impl.cc
+++ b/media/blink/webmediaplayer_impl.cc
@@ -1933,22 +1933,40 @@
   DCHECK(main_task_runner_->BelongsToCurrentThread());
 
   const PipelineStatistics stats = pipeline_.GetStatistics();
+  const int64_t data_source_memory_usage =
+      data_source_ ? data_source_->GetMemoryUsage() : 0;
   const int64_t current_memory_usage =
       stats.audio_memory_usage + stats.video_memory_usage +
-      (data_source_ ? data_source_->GetMemoryUsage() : 0) +
-      demuxer_memory_usage;
+      data_source_memory_usage + demuxer_memory_usage;
 
   // Note, this isn't entirely accurate, there may be VideoFrames held by the
   // compositor or other resources that we're unaware of.
 
   DVLOG(2) << "Memory Usage -- Audio: " << stats.audio_memory_usage
-           << ", Video: " << stats.video_memory_usage << ", DataSource: "
-           << (data_source_ ? data_source_->GetMemoryUsage() : 0)
+           << ", Video: " << stats.video_memory_usage
+           << ", DataSource: " << data_source_memory_usage
            << ", Demuxer: " << demuxer_memory_usage;
 
   const int64_t delta = current_memory_usage - last_reported_memory_usage_;
   last_reported_memory_usage_ = current_memory_usage;
   adjust_allocated_memory_cb_.Run(delta);
+
+  if (hasAudio()) {
+    UMA_HISTOGRAM_MEMORY_KB("Media.WebMediaPlayerImpl.Memory.Audio",
+                            stats.audio_memory_usage / 1024);
+  }
+  if (hasVideo()) {
+    UMA_HISTOGRAM_MEMORY_KB("Media.WebMediaPlayerImpl.Memory.Video",
+                            stats.video_memory_usage / 1024);
+  }
+  if (data_source_) {
+    UMA_HISTOGRAM_MEMORY_KB("Media.WebMediaPlayerImpl.Memory.DataSource",
+                            data_source_memory_usage / 1024);
+  }
+  if (demuxer_) {
+    UMA_HISTOGRAM_MEMORY_KB("Media.WebMediaPlayerImpl.Memory.Demuxer",
+                            demuxer_memory_usage / 1024);
+  }
 }
 
 void WebMediaPlayerImpl::ScheduleIdlePauseTimer() {
diff --git a/media/midi/BUILD.gn b/media/midi/BUILD.gn
index 38100e1..0dc5ed3 100644
--- a/media/midi/BUILD.gn
+++ b/media/midi/BUILD.gn
@@ -88,6 +88,8 @@
     "midi_port_info.h",
     "midi_scheduler.cc",
     "midi_scheduler.h",
+    "midi_service.cc",
+    "midi_service.h",
     "midi_switches.cc",
     "midi_switches.h",
   ]
diff --git a/media/midi/midi_manager.h b/media/midi/midi_manager.h
index f90a55b..27e24f9 100644
--- a/media/midi/midi_manager.h
+++ b/media/midi/midi_manager.h
@@ -28,6 +28,7 @@
 // A MidiManagerClient registers with the MidiManager to receive MIDI data.
 // See MidiManager::RequestAccess() and MidiManager::ReleaseAccess()
 // for details.
+// TODO(toyoshim): Consider to have a MidiServiceClient interface.
 class MIDI_EXPORT MidiManagerClient {
  public:
   virtual ~MidiManagerClient() {}
diff --git a/media/midi/midi_service.cc b/media/midi/midi_service.cc
new file mode 100644
index 0000000..0d01124
--- /dev/null
+++ b/media/midi/midi_service.cc
@@ -0,0 +1,47 @@
+// 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.
+
+#include "media/midi/midi_service.h"
+
+#include "media/midi/midi_manager.h"
+
+namespace midi {
+
+MidiService::MidiService(std::unique_ptr<MidiManager> manager) {
+  base::AutoLock lock(lock_);
+  if (manager.get())
+    manager_ = std::move(manager);
+  else
+    manager_.reset(MidiManager::Create());
+}
+
+MidiService::~MidiService() {
+  base::AutoLock lock(lock_);
+  manager_.reset();
+}
+
+void MidiService::Shutdown() {
+  base::AutoLock lock(lock_);
+  manager_->Shutdown();
+}
+
+void MidiService::StartSession(MidiManagerClient* client) {
+  base::AutoLock lock(lock_);
+  manager_->StartSession(client);
+}
+
+void MidiService::EndSession(MidiManagerClient* client) {
+  base::AutoLock lock(lock_);
+  manager_->EndSession(client);
+}
+
+void MidiService::DispatchSendMidiData(MidiManagerClient* client,
+                                       uint32_t port_index,
+                                       const std::vector<uint8_t>& data,
+                                       double timestamp) {
+  base::AutoLock lock(lock_);
+  manager_->DispatchSendMidiData(client, port_index, data, timestamp);
+}
+
+}  // namespace midi
diff --git a/media/midi/midi_service.h b/media/midi/midi_service.h
new file mode 100644
index 0000000..5b870d6c
--- /dev/null
+++ b/media/midi/midi_service.h
@@ -0,0 +1,53 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MEDIA_MIDI_MIDI_SERVICE_H_
+#define MEDIA_MIDI_MIDI_SERVICE_H_
+
+#include <stdint.h>
+
+#include <memory>
+#include <vector>
+
+#include "base/macros.h"
+#include "base/synchronization/lock.h"
+#include "media/midi/midi_export.h"
+#include "media/midi/midi_manager.h"
+
+namespace midi {
+
+// Manages MidiManager backends.  This class expects to be constructed and
+// destructed on the browser main thread, but methods can be called on both
+// the main thread and the I/O thread.
+class MIDI_EXPORT MidiService final {
+ public:
+  // |MidiManager| can be explicitly specified in the constructor for testing.
+  explicit MidiService(std::unique_ptr<MidiManager> manager = nullptr);
+  ~MidiService();
+
+  // Called on the browser main thread to notify the I/O thread will stop and
+  // the instance will be destructed on the main thread soon.
+  void Shutdown();
+
+  // A client calls StartSession() to receive and send MIDI data.
+  void StartSession(MidiManagerClient* client);
+
+  // A client calls EndSession() to stop receiving MIDI data.
+  void EndSession(MidiManagerClient* client);
+
+  // A client calls DispatchSendMidiData() to send MIDI data.
+  virtual void DispatchSendMidiData(MidiManagerClient* client,
+                                    uint32_t port_index,
+                                    const std::vector<uint8_t>& data,
+                                    double timestamp);
+
+  std::unique_ptr<MidiManager> manager_;
+  base::Lock lock_;
+
+  DISALLOW_COPY_AND_ASSIGN(MidiService);
+};
+
+}  // namespace midi
+
+#endif  // MEDIA_MIDI_MIDI_SERVICE_H_
diff --git a/services/ui/ws/window_manager_client_unittest.cc b/services/ui/ws/window_manager_client_unittest.cc
index d94a800..7f712dcf 100644
--- a/services/ui/ws/window_manager_client_unittest.cc
+++ b/services/ui/ws/window_manager_client_unittest.cc
@@ -652,7 +652,8 @@
 
   // Verify change from embedded makes it to parent.
   const gfx::Insets insets(1, 2, 3, 4);
-  embed_result->window_tree_host->SetClientArea(insets);
+  embed_result->window_tree_host->SetClientArea(insets,
+                                                std::vector<gfx::Rect>());
   std::unique_ptr<ClientAreaChange> client_area_change =
       WaitForClientAreaToChange();
   ASSERT_TRUE(client_area_change);
diff --git a/storage/browser/BUILD.gn b/storage/browser/BUILD.gn
index 8e91590..d701ae0 100644
--- a/storage/browser/BUILD.gn
+++ b/storage/browser/BUILD.gn
@@ -166,6 +166,8 @@
     "quota/quota_manager.h",
     "quota/quota_manager_proxy.cc",
     "quota/quota_manager_proxy.h",
+    "quota/quota_settings.cc",
+    "quota/quota_settings.h",
     "quota/quota_task.cc",
     "quota/quota_task.h",
     "quota/quota_temporary_storage_evictor.cc",
diff --git a/storage/browser/quota/client_usage_tracker.cc b/storage/browser/quota/client_usage_tracker.cc
index c9c46f6d1..c5a17a5 100644
--- a/storage/browser/quota/client_usage_tracker.cc
+++ b/storage/browser/quota/client_usage_tracker.cc
@@ -165,6 +165,15 @@
                                 AsWeakPtr(), origin));
 }
 
+int64_t ClientUsageTracker::GetCachedUsage() const {
+  int64_t usage = 0;
+  for (const auto& host_and_usage_map : cached_usage_by_host_) {
+    for (const auto& origin_and_usage : host_and_usage_map.second)
+      usage += origin_and_usage.second;
+  }
+  return usage;
+}
+
 void ClientUsageTracker::GetCachedHostsUsage(
     std::map<std::string, int64_t>* host_usage) const {
   DCHECK(host_usage);
diff --git a/storage/browser/quota/client_usage_tracker.h b/storage/browser/quota/client_usage_tracker.h
index 5b5a84b..2fbe765 100644
--- a/storage/browser/quota/client_usage_tracker.h
+++ b/storage/browser/quota/client_usage_tracker.h
@@ -52,6 +52,7 @@
   void GetGlobalUsage(const GlobalUsageCallback& callback);
   void GetHostUsage(const std::string& host, const UsageCallback& callback);
   void UpdateUsageCache(const GURL& origin, int64_t delta);
+  int64_t GetCachedUsage() const;
   void GetCachedHostsUsage(std::map<std::string, int64_t>* host_usage) const;
   void GetCachedOriginsUsage(std::map<GURL, int64_t>* origin_usage) const;
   void GetCachedOrigins(std::set<GURL>* origins) const;
diff --git a/storage/browser/quota/quota_manager.cc b/storage/browser/quota/quota_manager.cc
index 44ba5f1..b0bef465 100644
--- a/storage/browser/quota/quota_manager.cc
+++ b/storage/browser/quota/quota_manager.cc
@@ -13,6 +13,7 @@
 #include <memory>
 #include <utility>
 
+#include "base/barrier_closure.h"
 #include "base/bind.h"
 #include "base/bind_helpers.h"
 #include "base/command_line.h"
@@ -26,6 +27,7 @@
 #include "base/strings/string_number_conversions.h"
 #include "base/sys_info.h"
 #include "base/task_runner_util.h"
+#include "base/threading/thread_task_runner_handle.h"
 #include "base/time/time.h"
 #include "base/trace_event/trace_event.h"
 #include "net/base/url_util.h"
@@ -36,49 +38,36 @@
 #include "storage/browser/quota/usage_tracker.h"
 #include "storage/common/quota/quota_types.h"
 
-#define UMA_HISTOGRAM_MBYTES(name, sample)          \
-  UMA_HISTOGRAM_CUSTOM_COUNTS(                      \
-      (name), static_cast<int>((sample) / kMBytes), \
-      1, 10 * 1024 * 1024 /* 10TB */, 100)
-
 namespace storage {
 
 namespace {
 
 const int64_t kMBytes = 1024 * 1024;
 const int kMinutesInMilliSeconds = 60 * 1000;
-
 const int64_t kReportHistogramInterval = 60 * 60 * 1000;  // 1 hour
-const double kTemporaryQuotaRatioToAvail = 1.0 / 3.0;  // 33%
+
+#define UMA_HISTOGRAM_MBYTES(name, sample)                                     \
+  UMA_HISTOGRAM_CUSTOM_COUNTS((name), static_cast<int>((sample) / kMBytes), 1, \
+                              10 * 1024 * 1024 /* 10TB */, 100)
 
 }  // namespace
 
-// Arbitrary for now, but must be reasonably small so that
-// in-memory databases can fit.
-// TODO(kinuko): Refer SysInfo::AmountOfPhysicalMemory() to determine this.
-const int64_t QuotaManager::kIncognitoDefaultQuotaLimit = 100 * kMBytes;
-
 const int64_t QuotaManager::kNoLimit = INT64_MAX;
 
-const int QuotaManager::kPerHostTemporaryPortion = 5;  // 20%
-
 // Cap size for per-host persistent quota determined by the histogram.
 // This is a bit lax value because the histogram says nothing about per-host
 // persistent storage usage and we determined by global persistent storage
 // usage that is less than 10GB for almost all users.
 const int64_t QuotaManager::kPerHostPersistentQuotaLimit = 10 * 1024 * kMBytes;
 
+// Heuristics: assuming average cloud server allows a few Gigs storage
+// on the server side and the storage needs to be shared for user data
+// and by multiple apps.
+int64_t QuotaManager::kSyncableStorageDefaultHostQuota = 500 * kMBytes;
+
 const char QuotaManager::kDatabaseName[] = "QuotaManager";
 
 const int QuotaManager::kThresholdOfErrorsToBeBlacklisted = 3;
-
-// Preserve kMinimumPreserveForSystem disk space for system book-keeping
-// when returning the quota to unlimited apps/extensions.
-// TODO(kinuko): This should be like 10% of the actual disk space.
-// For now we simply use a constant as getting the disk size needs
-// platform-dependent code. (http://crbug.com/178976)
-int64_t QuotaManager::kMinimumPreserveForSystem = 1024 * kMBytes;
-
 const int QuotaManager::kEvictionIntervalInMilliSeconds =
     30 * kMinutesInMilliSeconds;
 
@@ -89,13 +78,17 @@
 const char QuotaManager::kEvictedOriginTimeSinceAccessHistogram[] =
     "Quota.EvictedOriginTimeSinceAccess";
 
-// Heuristics: assuming average cloud server allows a few Gigs storage
-// on the server side and the storage needs to be shared for user data
-// and by multiple apps.
-int64_t QuotaManager::kSyncableStorageDefaultHostQuota = 500 * kMBytes;
-
 namespace {
 
+bool IsSupportedType(StorageType type) {
+  return type != kStorageTypeTemporary || type != kStorageTypePersistent ||
+         type != kStorageTypeSyncable;
+}
+
+bool IsSupportedIncognitoType(StorageType type) {
+  return type != kStorageTypeTemporary || type != kStorageTypePersistent;
+}
+
 void CountOriginType(const std::set<GURL>& origins,
                      SpecialStoragePolicy* policy,
                      size_t* protected_origins,
@@ -116,17 +109,6 @@
   }
 }
 
-bool SetTemporaryGlobalOverrideQuotaOnDBThread(int64_t* new_quota,
-                                               QuotaDatabase* database) {
-  DCHECK(database);
-  if (!database->SetQuotaConfigValue(
-          QuotaDatabase::kTemporaryQuotaOverrideKey, *new_quota)) {
-    *new_quota = -1;
-    return false;
-  }
-  return true;
-}
-
 bool GetPersistentHostQuotaOnDBThread(const std::string& host,
                                       int64_t* quota,
                                       QuotaDatabase* database) {
@@ -145,17 +127,6 @@
   return false;
 }
 
-bool InitializeOnDBThread(int64_t* temporary_quota_override,
-                          int64_t* desired_available_space,
-                          QuotaDatabase* database) {
-  DCHECK(database);
-  database->GetQuotaConfigValue(QuotaDatabase::kTemporaryQuotaOverrideKey,
-                                temporary_quota_override);
-  database->GetQuotaConfigValue(QuotaDatabase::kDesiredAvailableSpaceKey,
-                                desired_available_space);
-  return true;
-}
-
 bool GetLRUOriginOnDBThread(StorageType type,
                             const std::set<GURL>& exceptions,
                             SpecialStoragePolicy* policy,
@@ -204,8 +175,8 @@
   return database->SetOriginLastEvictionTime(origin, type, now);
 }
 
-bool InitializeTemporaryOriginsInfoOnDBThread(const std::set<GURL>* origins,
-                                              QuotaDatabase* database) {
+bool BootstrapDatabaseOnDBThread(const std::set<GURL>* origins,
+                                 QuotaDatabase* database) {
   DCHECK(database);
   if (database->IsOriginDatabaseBootstrapped())
     return true;
@@ -234,277 +205,222 @@
   return database->SetOriginLastModifiedTime(origin, type, modified_time);
 }
 
-int64_t CalculateTemporaryGlobalQuota(int64_t global_limited_usage,
-                                      int64_t available_space) {
-  DCHECK_GE(global_limited_usage, 0);
-  int64_t avail_space = available_space;
-  if (avail_space <
-      std::numeric_limits<int64_t>::max() - global_limited_usage) {
-    // We basically calculate the temporary quota by
-    // [available_space + space_used_for_temp] * kTempQuotaRatio,
-    // but make sure we'll have no overflow.
-    avail_space += global_limited_usage;
-  }
-  int64_t pool_size = avail_space * kTemporaryQuotaRatioToAvail;
-  UMA_HISTOGRAM_MBYTES("Quota.GlobalTemporaryPoolSize", pool_size);
-  return pool_size;
-}
-
-void DispatchTemporaryGlobalQuotaCallback(
-    const QuotaCallback& callback,
-    QuotaStatusCode status,
-    const UsageAndQuota& usage_and_quota) {
-  if (status != kQuotaStatusOk) {
-    callback.Run(status, 0);
-    return;
-  }
-
-  callback.Run(status, CalculateTemporaryGlobalQuota(
-      usage_and_quota.global_limited_usage,
-      usage_and_quota.available_disk_space));
-}
-
-int64_t CalculateQuotaWithDiskSpace(int64_t available_disk_space,
-                                    int64_t usage,
-                                    int64_t quota) {
-  if (available_disk_space < QuotaManager::kMinimumPreserveForSystem) {
-    LOG(WARNING)
-        << "Running out of disk space for profile."
-        << " QuotaManager starts forbidding further quota consumption.";
-    return usage;
-  }
-
-  if (quota < usage) {
-    // No more space; cap the quota to the current usage.
-    return usage;
-  }
-
-  available_disk_space -= QuotaManager::kMinimumPreserveForSystem;
-  if (available_disk_space < quota - usage)
-    return available_disk_space + usage;
-
-  return quota;
-}
-
-int64_t CalculateTemporaryHostQuota(int64_t host_usage,
-                                    int64_t global_quota,
-                                    int64_t global_limited_usage) {
-  DCHECK_GE(global_limited_usage, 0);
-  int64_t host_quota = global_quota / QuotaManager::kPerHostTemporaryPortion;
-  if (global_limited_usage > global_quota)
-    host_quota = std::min(host_quota, host_usage);
-  return host_quota;
-}
-
-void DispatchUsageAndQuotaForWebApps(
-    StorageType type,
-    bool is_incognito,
-    bool is_unlimited,
-    bool can_query_disk_size,
-    const QuotaManager::GetUsageAndQuotaCallback& callback,
-    QuotaStatusCode status,
-    const UsageAndQuota& usage_and_quota) {
-  if (status != kQuotaStatusOk) {
-    callback.Run(status, 0, 0);
-    return;
-  }
-
-  int64_t usage = usage_and_quota.usage;
-  int64_t quota = usage_and_quota.quota;
-
-  if (type == kStorageTypeTemporary && !is_unlimited) {
-    quota = CalculateTemporaryHostQuota(
-        usage, quota, usage_and_quota.global_limited_usage);
-  }
-
-  if (is_incognito) {
-    quota = std::min(quota, QuotaManager::kIncognitoDefaultQuotaLimit);
-    callback.Run(status, usage, quota);
-    return;
-  }
-
-  // For apps with unlimited permission or can_query_disk_size is true (and not
-  // in incognito mode).
-  // We assume we can expose the actual disk size for them and cap the quota by
-  // the available disk space.
-  if (is_unlimited || can_query_disk_size) {
-    quota = CalculateQuotaWithDiskSpace(
-        usage_and_quota.available_disk_space,
-        usage, quota);
-  }
-
-  callback.Run(status, usage, quota);
-
-  if (type == kStorageTypeTemporary && !is_unlimited)
-    UMA_HISTOGRAM_MBYTES("Quota.QuotaForOrigin", quota);
-}
-
 }  // namespace
 
-UsageAndQuota::UsageAndQuota()
-    : usage(0),
-      global_limited_usage(0),
-      quota(0),
-      available_disk_space(0) {
-}
-
-UsageAndQuota::UsageAndQuota(int64_t usage,
-                             int64_t global_limited_usage,
-                             int64_t quota,
-                             int64_t available_disk_space)
-    : usage(usage),
-      global_limited_usage(global_limited_usage),
-      quota(quota),
-      available_disk_space(available_disk_space) {}
-
-class UsageAndQuotaCallbackDispatcher
-    : public QuotaTask,
-      public base::SupportsWeakPtr<UsageAndQuotaCallbackDispatcher> {
+class QuotaManager::UsageAndQuotaHelper : public QuotaTask {
  public:
-  explicit UsageAndQuotaCallbackDispatcher(QuotaManager* manager)
+  UsageAndQuotaHelper(QuotaManager* manager,
+                      const GURL& origin,
+                      StorageType type,
+                      bool is_unlimited,
+                      bool is_incognito,
+                      const UsageAndQuotaCallback& callback)
       : QuotaTask(manager),
-        has_usage_(false),
-        has_global_limited_usage_(false),
-        has_quota_(false),
-        has_available_disk_space_(false),
-        status_(kQuotaStatusUnknown),
-        usage_and_quota_(-1, -1, -1, -1),
-        waiting_callbacks_(1) {}
+        origin_(origin),
+        callback_(callback),
+        type_(type),
+        is_unlimited_(is_unlimited),
+        is_incognito_(is_incognito),
+        weak_factory_(this) {}
 
-  ~UsageAndQuotaCallbackDispatcher() override {}
-
-  void WaitForResults(const QuotaManager::UsageAndQuotaCallback& callback) {
-    callback_ = callback;
-    Start();
-  }
-
-  void set_usage(int64_t usage) {
-    usage_and_quota_.usage = usage;
-    has_usage_ = true;
-  }
-
-  void set_global_limited_usage(int64_t global_limited_usage) {
-    usage_and_quota_.global_limited_usage = global_limited_usage;
-    has_global_limited_usage_ = true;
-  }
-
-  void set_quota(int64_t quota) {
-    usage_and_quota_.quota = quota;
-    has_quota_ = true;
-  }
-
-  void set_available_disk_space(int64_t available_disk_space) {
-    usage_and_quota_.available_disk_space = available_disk_space;
-    has_available_disk_space_ = true;
-  }
-
-  UsageCallback GetHostUsageCallback() {
-    ++waiting_callbacks_;
-    has_usage_ = true;
-    return base::Bind(&UsageAndQuotaCallbackDispatcher::DidGetHostUsage,
-                      AsWeakPtr());
-  }
-
-  UsageCallback GetGlobalLimitedUsageCallback() {
-    ++waiting_callbacks_;
-    has_global_limited_usage_ = true;
-    return base::Bind(
-        &UsageAndQuotaCallbackDispatcher::DidGetGlobalLimitedUsage,
-        AsWeakPtr());
-  }
-
-  QuotaCallback GetQuotaCallback() {
-    ++waiting_callbacks_;
-    has_quota_ = true;
-    return base::Bind(&UsageAndQuotaCallbackDispatcher::DidGetQuota,
-                      AsWeakPtr());
-  }
-
-  QuotaCallback GetAvailableSpaceCallback() {
-    ++waiting_callbacks_;
-    has_available_disk_space_ = true;
-    return base::Bind(&UsageAndQuotaCallbackDispatcher::DidGetAvailableSpace,
-                      AsWeakPtr());
-  }
-
- private:
-  void DidGetHostUsage(int64_t usage) {
-    if (status_ == kQuotaStatusUnknown)
-      status_ = kQuotaStatusOk;
-    usage_and_quota_.usage = usage;
-    CheckCompleted();
-  }
-
-  void DidGetGlobalLimitedUsage(int64_t limited_usage) {
-    if (status_ == kQuotaStatusUnknown)
-      status_ = kQuotaStatusOk;
-    usage_and_quota_.global_limited_usage = limited_usage;
-    CheckCompleted();
-  }
-
-  void DidGetQuota(QuotaStatusCode status, int64_t quota) {
-    if (status_ == kQuotaStatusUnknown || status_ == kQuotaStatusOk)
-      status_ = status;
-    usage_and_quota_.quota = quota;
-    CheckCompleted();
-  }
-
-  void DidGetAvailableSpace(QuotaStatusCode status, int64_t space) {
-    // crbug.com/349708
-    TRACE_EVENT0(
-        "io", "UsageAndQuotaCallbackDispatcher::DidGetAvailableSpace");
-
-    DCHECK_GE(space, 0);
-    if (status_ == kQuotaStatusUnknown || status_ == kQuotaStatusOk)
-      status_ = status;
-    usage_and_quota_.available_disk_space = space;
-    CheckCompleted();
-  }
-
+ protected:
   void Run() override {
-    // We initialize waiting_callbacks to 1 so that we won't run
-    // the completion callback until here even some of the callbacks
-    // are dispatched synchronously.
-    CheckCompleted();
+    // Start the async process of gathering the info we need.
+    // Gather 4 pieces of info before computing an answer:
+    // settings, device_storage_capacity, host_usage, and host_quota.
+    base::Closure barrier = base::BarrierClosure(
+        4, base::Bind(&UsageAndQuotaHelper::OnBarrierComplete,
+                      weak_factory_.GetWeakPtr()));
+
+    std::string host = net::GetHostOrSpecFromURL(origin_);
+
+    manager()->GetQuotaSettings(base::Bind(&UsageAndQuotaHelper::OnGotSettings,
+                                           weak_factory_.GetWeakPtr(),
+                                           barrier));
+    manager()->GetStorageCapacity(
+        base::Bind(&UsageAndQuotaHelper::OnGotCapacity,
+                   weak_factory_.GetWeakPtr(), barrier));
+    manager()->GetHostUsage(host, type_,
+                            base::Bind(&UsageAndQuotaHelper::OnGotHostUsage,
+                                       weak_factory_.GetWeakPtr(), barrier));
+
+    // Determine host_quota differently depending on type.
+    if (is_unlimited_) {
+      SetDesiredHostQuota(barrier, kQuotaStatusOk, kNoLimit);
+    } else if (type_ == kStorageTypeSyncable) {
+      SetDesiredHostQuota(barrier, kQuotaStatusOk,
+                          kSyncableStorageDefaultHostQuota);
+    } else if (type_ == kStorageTypePersistent) {
+      manager()->GetPersistentHostQuota(
+          host, base::Bind(&UsageAndQuotaHelper::SetDesiredHostQuota,
+                           weak_factory_.GetWeakPtr(), barrier));
+    } else {
+      DCHECK_EQ(kStorageTypeTemporary, type_);
+      // For temporary storge,  OnGotSettings will set the host quota.
+    }
   }
 
   void Aborted() override {
-    callback_.Run(kQuotaErrorAbort, UsageAndQuota());
+    weak_factory_.InvalidateWeakPtrs();
+    callback_.Run(kQuotaErrorAbort, 0, 0);
     DeleteSoon();
   }
 
   void Completed() override {
-    // crbug.com/349708
-    TRACE_EVENT0("io", "UsageAndQuotaCallbackDispatcher::Completed");
+    weak_factory_.InvalidateWeakPtrs();
 
-    DCHECK(!has_usage_ || usage_and_quota_.usage >= 0);
-    DCHECK(!has_global_limited_usage_ ||
-           usage_and_quota_.global_limited_usage >= 0);
-    DCHECK(!has_quota_ || usage_and_quota_.quota >= 0);
-    DCHECK(!has_available_disk_space_ ||
-           usage_and_quota_.available_disk_space >= 0);
-
-    callback_.Run(status_, usage_and_quota_);
+    // Constrain the desired |host_quota| to something that fits.
+    // If available space is too low, cap usage at current levels.
+    // If it's close to being too low, cap growth to avoid it getting too low.
+    int64_t host_quota =
+        std::min(desired_host_quota_,
+                 host_usage_ +
+                     std::max(INT64_C(0), available_space_ -
+                                              settings_.must_remain_available));
+    callback_.Run(kQuotaStatusOk, host_usage_, host_quota);
+    if (type_ == kStorageTypeTemporary && !is_incognito_ && !is_unlimited_)
+      UMA_HISTOGRAM_MBYTES("Quota.QuotaForOrigin", host_quota);
     DeleteSoon();
   }
 
-  void CheckCompleted() {
-    if (--waiting_callbacks_ <= 0)
-      CallCompleted();
+ private:
+  QuotaManager* manager() const {
+    return static_cast<QuotaManager*>(observer());
   }
 
-  // For sanity checks, they're checked only when DCHECK is on.
-  bool has_usage_;
-  bool has_global_limited_usage_;
-  bool has_quota_;
-  bool has_available_disk_space_;
+  void OnGotSettings(const base::Closure& barrier_closure,
+                     const QuotaSettings& settings) {
+    settings_ = settings;
+    barrier_closure.Run();
+    if (type_ == kStorageTypeTemporary && !is_unlimited_) {
+      SetDesiredHostQuota(barrier_closure, kQuotaStatusOk,
+                          settings.per_host_quota);
+    }
+  }
 
-  QuotaStatusCode status_;
-  UsageAndQuota usage_and_quota_;
+  void OnGotCapacity(const base::Closure& barrier_closure,
+                     int64_t total_space,
+                     int64_t available_space) {
+    total_space_ = total_space;
+    available_space_ = available_space;
+    barrier_closure.Run();
+  }
+
+  void OnGotHostUsage(const base::Closure& barrier_closure, int64_t usage) {
+    host_usage_ = usage;
+    barrier_closure.Run();
+  }
+
+  void SetDesiredHostQuota(const base::Closure& barrier_closure,
+                           QuotaStatusCode status,
+                           int64_t quota) {
+    desired_host_quota_ = quota;
+    barrier_closure.Run();
+  }
+
+  void OnBarrierComplete() { CallCompleted(); }
+
+  GURL origin_;
   QuotaManager::UsageAndQuotaCallback callback_;
-  int waiting_callbacks_;
+  StorageType type_;
+  bool is_unlimited_;
+  bool is_incognito_;
+  int64_t available_space_ = 0;
+  int64_t total_space_ = 0;
+  int64_t desired_host_quota_ = 0;
+  int64_t host_usage_ = 0;
+  QuotaSettings settings_;
+  base::WeakPtrFactory<UsageAndQuotaHelper> weak_factory_;
+  DISALLOW_COPY_AND_ASSIGN(UsageAndQuotaHelper);
+};
 
-  DISALLOW_COPY_AND_ASSIGN(UsageAndQuotaCallbackDispatcher);
+// Helper to asychronously gather information needed at the start of an
+// eviction round.
+class QuotaManager::EvictionRoundInfoHelper : public QuotaTask {
+ public:
+  EvictionRoundInfoHelper(QuotaManager* manager,
+                          const EvictionRoundInfoCallback& callback)
+      : QuotaTask(manager), callback_(callback), weak_factory_(this) {}
+
+ protected:
+  void Run() override {
+    // Gather 2 pieces of info before deciding if we need to get GlobalUsage:
+    // settings and device_storage_capacity.
+    base::Closure barrier = base::BarrierClosure(
+        2, base::Bind(&EvictionRoundInfoHelper::OnBarrierComplete,
+                      weak_factory_.GetWeakPtr()));
+
+    manager()->GetQuotaSettings(
+        base::Bind(&EvictionRoundInfoHelper::OnGotSettings,
+                   weak_factory_.GetWeakPtr(), barrier));
+    manager()->GetStorageCapacity(
+        base::Bind(&EvictionRoundInfoHelper::OnGotCapacity,
+                   weak_factory_.GetWeakPtr(), barrier));
+  }
+
+  void Aborted() override {
+    weak_factory_.InvalidateWeakPtrs();
+    callback_.Run(kQuotaErrorAbort, QuotaSettings(), 0, 0, 0, false);
+    DeleteSoon();
+  }
+
+  void Completed() override {
+    weak_factory_.InvalidateWeakPtrs();
+    callback_.Run(kQuotaStatusOk, settings_, available_space_, total_space_,
+                  global_usage_, global_usage_is_complete_);
+    DeleteSoon();
+  }
+
+ private:
+  QuotaManager* manager() const {
+    return static_cast<QuotaManager*>(observer());
+  }
+
+  void OnGotSettings(const base::Closure& barrier_closure,
+                     const QuotaSettings& settings) {
+    settings_ = settings;
+    barrier_closure.Run();
+  }
+
+  void OnGotCapacity(const base::Closure& barrier_closure,
+                     int64_t total_space,
+                     int64_t available_space) {
+    total_space_ = total_space;
+    available_space_ = available_space;
+    barrier_closure.Run();
+  }
+
+  void OnBarrierComplete() {
+    // Avoid computing the full current_usage when there's no pressure.
+    int64_t consumed_space = total_space_ - available_space_;
+    if (consumed_space < settings_.pool_size &&
+        available_space_ > settings_.must_remain_available) {
+      DCHECK(!global_usage_is_complete_);
+      global_usage_ =
+          manager()->GetUsageTracker(kStorageTypeTemporary)->GetCachedUsage();
+      CallCompleted();
+      return;
+    }
+    manager()->GetGlobalUsage(
+        kStorageTypeTemporary,
+        base::Bind(&EvictionRoundInfoHelper::OnGotGlobalUsage,
+                   weak_factory_.GetWeakPtr()));
+  }
+
+  void OnGotGlobalUsage(int64_t usage, int64_t unlimited_usage) {
+    global_usage_ = std::max(INT64_C(0), usage - unlimited_usage);
+    global_usage_is_complete_ = true;
+    CallCompleted();
+  }
+
+  EvictionRoundInfoCallback callback_;
+  QuotaSettings settings_;
+  int64_t available_space_ = 0;
+  int64_t total_space_ = 0;
+  int64_t global_usage_ = 0;
+  bool global_usage_is_complete_ = false;
+  base::WeakPtrFactory<EvictionRoundInfoHelper> weak_factory_;
+  DISALLOW_COPY_AND_ASSIGN(EvictionRoundInfoHelper);
 };
 
 class QuotaManager::GetUsageInfoTask : public QuotaTask {
@@ -866,7 +782,8 @@
     const base::FilePath& profile_path,
     const scoped_refptr<base::SingleThreadTaskRunner>& io_thread,
     const scoped_refptr<base::SequencedTaskRunner>& db_thread,
-    const scoped_refptr<SpecialStoragePolicy>& special_storage_policy)
+    const scoped_refptr<SpecialStoragePolicy>& special_storage_policy,
+    const GetQuotaSettingsFunc& get_settings_function)
     : is_incognito_(is_incognito),
       profile_path_(profile_path),
       proxy_(new QuotaManagerProxy(this, io_thread)),
@@ -874,13 +791,25 @@
       eviction_disabled_(false),
       io_thread_(io_thread),
       db_thread_(db_thread),
+      get_settings_function_(get_settings_function),
       is_getting_eviction_origin_(false),
-      temporary_quota_initialized_(false),
-      temporary_quota_override_(-1),
       special_storage_policy_(special_storage_policy),
       get_volume_info_fn_(&QuotaManager::GetVolumeInfo),
       storage_monitor_(new StorageMonitor(this)),
-      weak_factory_(this) {}
+      weak_factory_(this) {
+  DCHECK_EQ(settings_.refresh_interval, base::TimeDelta::Max());
+  if (!get_settings_function.is_null()) {
+    // Reset the interval to ensure we use the get_settings_function
+    // the first times settings_ is needed.
+    settings_.refresh_interval = base::TimeDelta();
+    get_settings_task_runner_ = base::ThreadTaskRunnerHandle::Get();
+  }
+}
+
+void QuotaManager::SetQuotaSettings(const QuotaSettings& settings) {
+  settings_ = settings;
+  settings_timestamp_ = base::TimeTicks::Now();
+}
 
 void QuotaManager::GetUsageInfo(const GetUsageInfoCallback& callback) {
   LazyInitialize();
@@ -891,61 +820,28 @@
 void QuotaManager::GetUsageAndQuotaForWebApps(
     const GURL& origin,
     StorageType type,
-    const GetUsageAndQuotaCallback& callback) {
-  // TODO(pkasting): Remove ScopedTracker below once crbug.com/477117 is fixed.
-  tracked_objects::ScopedTracker tracking_profile(
-      FROM_HERE_WITH_EXPLICIT_FUNCTION(
-          "477117 QuotaManager::GetUsageAndQuotaForWebApps"));
-  if (type != kStorageTypeTemporary &&
-      type != kStorageTypePersistent &&
-      type != kStorageTypeSyncable) {
+    const UsageAndQuotaCallback& callback) {
+  DCHECK(origin == origin.GetOrigin());
+  if (!IsSupportedType(type) ||
+      (is_incognito_ && !IsSupportedIncognitoType(type))) {
     callback.Run(kQuotaErrorNotSupported, 0, 0);
     return;
   }
-
-  DCHECK(origin == origin.GetOrigin());
   LazyInitialize();
-
-  bool unlimited = IsStorageUnlimited(origin, type);
-  bool can_query_disk_size = CanQueryDiskSize(origin);
-
-  UsageAndQuotaCallbackDispatcher* dispatcher =
-      new UsageAndQuotaCallbackDispatcher(this);
-
-  if (unlimited) {
-    dispatcher->set_quota(kNoLimit);
-  } else {
-    if (type == kStorageTypeTemporary) {
-      GetUsageTracker(type)->GetGlobalLimitedUsage(
-          dispatcher->GetGlobalLimitedUsageCallback());
-      GetTemporaryGlobalQuota(dispatcher->GetQuotaCallback());
-    } else if (type == kStorageTypePersistent) {
-      GetPersistentHostQuota(net::GetHostOrSpecFromURL(origin),
-                             dispatcher->GetQuotaCallback());
-    } else {
-      dispatcher->set_quota(kSyncableStorageDefaultHostQuota);
-    }
-  }
-
-  DCHECK(GetUsageTracker(type));
-  GetUsageTracker(type)->GetHostUsage(net::GetHostOrSpecFromURL(origin),
-                                      dispatcher->GetHostUsageCallback());
-
-  if (!is_incognito_ && (unlimited || can_query_disk_size))
-    GetAvailableSpace(dispatcher->GetAvailableSpaceCallback());
-
-  dispatcher->WaitForResults(base::Bind(
-      &DispatchUsageAndQuotaForWebApps,
-      type, is_incognito_, unlimited, can_query_disk_size,
-      callback));
+  UsageAndQuotaHelper* helper = new UsageAndQuotaHelper(
+      this, origin, type, IsStorageUnlimited(origin, type), is_incognito_,
+      callback);
+  helper->Start();
 }
 
-void QuotaManager::GetUsageAndQuota(
-    const GURL& origin, StorageType type,
-    const GetUsageAndQuotaCallback& callback) {
+void QuotaManager::GetUsageAndQuota(const GURL& origin,
+                                    StorageType type,
+                                    const UsageAndQuotaCallback& callback) {
   DCHECK(origin == origin.GetOrigin());
 
   if (IsStorageUnlimited(origin, type)) {
+    // TODO(michaeln): This seems like a non-obvious odd behavior, probably for
+    // apps/extensions, but it would be good to elimiate this special case.
     callback.Run(kQuotaStatusOk, 0, kNoLimit);
     return;
   }
@@ -991,11 +887,6 @@
   GetUsageTracker(type)->SetUsageCacheEnabled(client_id, origin, enabled);
 }
 
-void QuotaManager::SetTemporaryStorageEvictionPolicy(
-    std::unique_ptr<QuotaEvictionPolicy> policy) {
-  temporary_storage_eviction_policy_ = std::move(policy);
-}
-
 void QuotaManager::DeleteOriginData(const GURL& origin,
                                     StorageType type,
                                     int quota_client_mask,
@@ -1008,7 +899,6 @@
                                   int quota_client_mask,
                                   const StatusCallback& callback) {
   LazyInitialize();
-
   if (host.empty() || clients_.empty()) {
     callback.Run(kQuotaStatusOk);
     return;
@@ -1019,72 +909,6 @@
   deleter->Start();
 }
 
-void QuotaManager::GetAvailableSpace(const AvailableSpaceCallback& callback) {
-  if (!available_space_callbacks_.Add(callback))
-    return;
-  // crbug.com/349708
-  TRACE_EVENT0("io", "QuotaManager::GetAvailableSpace");
-
-  PostTaskAndReplyWithResult(
-      db_thread_.get(),
-      FROM_HERE,
-      base::Bind(&QuotaManager::CallGetAmountOfFreeDiskSpace,
-                 get_volume_info_fn_, profile_path_),
-      base::Bind(&QuotaManager::DidGetAvailableSpace,
-                 weak_factory_.GetWeakPtr()));
-}
-
-void QuotaManager::GetTemporaryGlobalQuota(const QuotaCallback& callback) {
-  LazyInitialize();
-  if (!temporary_quota_initialized_) {
-    db_initialization_callbacks_.Add(base::Bind(
-        &QuotaManager::GetTemporaryGlobalQuota,
-        weak_factory_.GetWeakPtr(), callback));
-    return;
-  }
-
-  if (temporary_quota_override_ > 0) {
-    callback.Run(kQuotaStatusOk, temporary_quota_override_);
-    return;
-  }
-
-  UsageAndQuotaCallbackDispatcher* dispatcher =
-      new UsageAndQuotaCallbackDispatcher(this);
-  GetUsageTracker(kStorageTypeTemporary)->
-      GetGlobalLimitedUsage(dispatcher->GetGlobalLimitedUsageCallback());
-  GetAvailableSpace(dispatcher->GetAvailableSpaceCallback());
-  dispatcher->WaitForResults(
-      base::Bind(&DispatchTemporaryGlobalQuotaCallback, callback));
-}
-
-void QuotaManager::SetTemporaryGlobalOverrideQuota(
-    int64_t new_quota,
-    const QuotaCallback& callback) {
-  LazyInitialize();
-
-  if (new_quota < 0) {
-    if (!callback.is_null())
-      callback.Run(kQuotaErrorInvalidModification, -1);
-    return;
-  }
-
-  if (db_disabled_) {
-    if (!callback.is_null())
-      callback.Run(kQuotaErrorInvalidAccess, -1);
-    return;
-  }
-
-  int64_t* new_quota_ptr = new int64_t(new_quota);
-  PostTaskAndReplyWithResultForDBThread(
-      FROM_HERE,
-      base::Bind(&SetTemporaryGlobalOverrideQuotaOnDBThread,
-                 base::Unretained(new_quota_ptr)),
-      base::Bind(&QuotaManager::DidSetTemporaryGlobalOverrideQuota,
-                 weak_factory_.GetWeakPtr(),
-                 callback,
-                 base::Owned(new_quota_ptr)));
-}
-
 void QuotaManager::GetPersistentHostQuota(const std::string& host,
                                           const QuotaCallback& callback) {
   LazyInitialize();
@@ -1126,10 +950,8 @@
     return;
   }
 
-  if (kPerHostPersistentQuotaLimit < new_quota) {
-    // Cap the requested size at the per-host quota limit.
-    new_quota = kPerHostPersistentQuotaLimit;
-  }
+  // Cap the requested size at the per-host quota limit.
+  new_quota = std::min(new_quota, kPerHostPersistentQuotaLimit);
 
   if (db_disabled_) {
     callback.Run(kQuotaErrorInvalidAccess, -1);
@@ -1289,7 +1111,7 @@
 void QuotaManager::LazyInitialize() {
   DCHECK(io_thread_->BelongsToCurrentThread());
   if (database_) {
-    // Initialization seems to be done already.
+    // Already initialized.
     return;
   }
 
@@ -1307,17 +1129,45 @@
       clients_, kStorageTypeSyncable, special_storage_policy_.get(),
       storage_monitor_.get()));
 
-  int64_t* temporary_quota_override = new int64_t(-1);
-  int64_t* desired_available_space = new int64_t(-1);
+  if (!is_incognito_) {
+    histogram_timer_.Start(
+        FROM_HERE, base::TimeDelta::FromMilliseconds(kReportHistogramInterval),
+        this, &QuotaManager::ReportHistogram);
+  }
+
+  base::PostTaskAndReplyWithResult(
+      db_thread_.get(), FROM_HERE,
+      base::Bind(&QuotaDatabase::IsOriginDatabaseBootstrapped,
+                 base::Unretained(database_.get())),
+      base::Bind(&QuotaManager::FinishLazyInitialize,
+                 weak_factory_.GetWeakPtr()));
+}
+
+void QuotaManager::FinishLazyInitialize(bool is_database_bootstrapped) {
+  is_database_bootstrapped_ = is_database_bootstrapped;
+  StartEviction();
+}
+
+void QuotaManager::BootstrapDatabaseForEviction(
+    const GetOriginCallback& did_get_origin_callback,
+    int64_t usage,
+    int64_t unlimited_usage) {
+  // The usage cache should be fully populated now so we can
+  // seed the database with origins we know about.
+  std::set<GURL>* origins = new std::set<GURL>;
+  temporary_usage_tracker_->GetCachedOrigins(origins);
   PostTaskAndReplyWithResultForDBThread(
-      FROM_HERE,
-      base::Bind(&InitializeOnDBThread,
-                 base::Unretained(temporary_quota_override),
-                 base::Unretained(desired_available_space)),
-      base::Bind(&QuotaManager::DidInitialize,
-                 weak_factory_.GetWeakPtr(),
-                 base::Owned(temporary_quota_override),
-                 base::Owned(desired_available_space)));
+      FROM_HERE, base::Bind(&BootstrapDatabaseOnDBThread, base::Owned(origins)),
+      base::Bind(&QuotaManager::DidBootstrapDatabase,
+                 weak_factory_.GetWeakPtr(), did_get_origin_callback));
+}
+
+void QuotaManager::DidBootstrapDatabase(
+    const GetOriginCallback& did_get_origin_callback,
+    bool success) {
+  is_database_bootstrapped_ = success;
+  DidDatabaseWork(success);
+  GetLRUOrigin(kStorageTypeTemporary, did_get_origin_callback);
 }
 
 void QuotaManager::RegisterClient(QuotaClient* client) {
@@ -1412,11 +1262,10 @@
 
 void QuotaManager::StartEviction() {
   DCHECK(!temporary_storage_evictor_.get());
+  if (eviction_disabled_)
+    return;
   temporary_storage_evictor_.reset(new QuotaTemporaryStorageEvictor(
       this, kEvictionIntervalInMilliSeconds));
-  if (desired_available_space_ >= 0)
-    temporary_storage_evictor_->set_min_available_disk_space_to_start_eviction(
-        desired_available_space_);
   temporary_storage_evictor_->Start();
 }
 
@@ -1602,26 +1451,21 @@
       base::Bind(&QuotaManager::DidGetEvictionOrigin,
                  weak_factory_.GetWeakPtr(), callback);
 
-  if (type == kStorageTypeTemporary && temporary_storage_eviction_policy_) {
-    std::map<GURL, int64_t> usage_map;
-    // The cached origins are populated by the prior call to
-    // GetUsageAndQuotaForEviction().
-    GetUsageTracker(kStorageTypeTemporary)->GetCachedOriginsUsage(&usage_map);
-
-    temporary_storage_eviction_policy_->GetEvictionOrigin(
-        special_storage_policy_, GetEvictionOriginExceptions(extra_exceptions),
-        usage_map, global_quota, did_get_origin_callback);
-
+  if (!is_database_bootstrapped_ && !eviction_disabled_) {
+    // Once bootstrapped, GetLRUOrigin will be called.
+    GetGlobalUsage(
+        kStorageTypeTemporary,
+        base::Bind(&QuotaManager::BootstrapDatabaseForEviction,
+                   weak_factory_.GetWeakPtr(), did_get_origin_callback));
     return;
   }
 
-  // TODO(calamity): convert LRU origin retrieval into a QuotaEvictionPolicy.
   GetLRUOrigin(type, did_get_origin_callback);
 }
 
 void QuotaManager::EvictOriginData(const GURL& origin,
                                    StorageType type,
-                                   const EvictOriginDataCallback& callback) {
+                                   const StatusCallback& callback) {
   DCHECK(io_thread_->BelongsToCurrentThread());
   DCHECK_EQ(type, kStorageTypeTemporary);
 
@@ -1634,47 +1478,12 @@
                                       weak_factory_.GetWeakPtr()));
 }
 
-void QuotaManager::GetUsageAndQuotaForEviction(
-    const UsageAndQuotaCallback& callback) {
-  // crbug.com/349708
-  TRACE_EVENT0("io", "QuotaManager::GetUsageAndQuotaForEviction");
-
+void QuotaManager::GetEvictionRoundInfo(
+    const EvictionRoundInfoCallback& callback) {
   DCHECK(io_thread_->BelongsToCurrentThread());
   LazyInitialize();
-
-  UsageAndQuotaCallbackDispatcher* dispatcher =
-      new UsageAndQuotaCallbackDispatcher(this);
-  GetUsageTracker(kStorageTypeTemporary)
-      ->GetGlobalLimitedUsage(dispatcher->GetGlobalLimitedUsageCallback());
-  GetTemporaryGlobalQuota(dispatcher->GetQuotaCallback());
-  GetAvailableSpace(dispatcher->GetAvailableSpaceCallback());
-  dispatcher->WaitForResults(callback);
-}
-
-void QuotaManager::AsyncGetVolumeInfo(
-    const VolumeInfoCallback& callback) {
-  DCHECK(io_thread_->BelongsToCurrentThread());
-  uint64_t* available_space = new uint64_t(0);
-  uint64_t* total_space = new uint64_t(0);
-  PostTaskAndReplyWithResult(
-      db_thread_.get(),
-      FROM_HERE,
-      base::Bind(get_volume_info_fn_,
-                 profile_path_,
-                 base::Unretained(available_space),
-                 base::Unretained(total_space)),
-      base::Bind(&QuotaManager::DidGetVolumeInfo,
-                 weak_factory_.GetWeakPtr(),
-                 callback,
-                 base::Owned(available_space),
-                 base::Owned(total_space)));
-}
-
-void QuotaManager::DidGetVolumeInfo(
-    const VolumeInfoCallback& callback,
-    uint64_t* available_space, uint64_t* total_space, bool success) {
-  DCHECK(io_thread_->BelongsToCurrentThread());
-  callback.Run(success, *available_space, *total_space);
+  EvictionRoundInfoHelper* helper = new EvictionRoundInfoHelper(this, callback);
+  helper->Start();
 }
 
 void QuotaManager::GetLRUOrigin(StorageType type,
@@ -1699,28 +1508,12 @@
                  base::Owned(url)));
 }
 
-void QuotaManager::DidSetTemporaryGlobalOverrideQuota(
-    const QuotaCallback& callback,
-    const int64_t* new_quota,
-    bool success) {
-  QuotaStatusCode status = kQuotaErrorInvalidAccess;
-  DidDatabaseWork(success);
-  if (success) {
-    temporary_quota_override_ = *new_quota;
-    status = kQuotaStatusOk;
-  }
-
-  if (callback.is_null())
-    return;
-
-  callback.Run(status, *new_quota);
-}
-
 void QuotaManager::DidGetPersistentHostQuota(const std::string& host,
                                              const int64_t* quota,
                                              bool success) {
   DidDatabaseWork(success);
-  persistent_host_quota_callbacks_.Run(host, kQuotaStatusOk, *quota);
+  persistent_host_quota_callbacks_.Run(
+      host, kQuotaStatusOk, std::min(*quota, kPerHostPersistentQuotaLimit));
 }
 
 void QuotaManager::DidSetPersistentHostQuota(const std::string& host,
@@ -1731,27 +1524,6 @@
   callback.Run(success ? kQuotaStatusOk : kQuotaErrorInvalidAccess, *new_quota);
 }
 
-void QuotaManager::DidInitialize(int64_t* temporary_quota_override,
-                                 int64_t* desired_available_space,
-                                 bool success) {
-  temporary_quota_override_ = *temporary_quota_override;
-  desired_available_space_ = *desired_available_space;
-  temporary_quota_initialized_ = true;
-  DidDatabaseWork(success);
-
-  if (!is_incognito_) {
-    histogram_timer_.Start(FROM_HERE,
-                           base::TimeDelta::FromMilliseconds(
-                               kReportHistogramInterval),
-                           this, &QuotaManager::ReportHistogram);
-  }
-
-  db_initialization_callbacks_.Run();
-  GetTemporaryGlobalQuota(
-      base::Bind(&QuotaManager::DidGetInitialTemporaryGlobalQuota,
-                 weak_factory_.GetWeakPtr(), base::TimeTicks::Now()));
-}
-
 void QuotaManager::DidGetLRUOrigin(const GURL* origin,
                                    bool success) {
   DidDatabaseWork(success);
@@ -1760,41 +1532,83 @@
   lru_origin_callback_.Reset();
 }
 
-void QuotaManager::DidGetInitialTemporaryGlobalQuota(
-    base::TimeTicks start_ticks,
-    QuotaStatusCode status,
-    int64_t quota_unused) {
-  UMA_HISTOGRAM_LONG_TIMES(
-      "Quota.TimeToInitializeGlobalQuota",
-      base::TimeTicks::Now() - start_ticks);
+namespace {
+void DidGetSettingsThreadAdapter(base::TaskRunner* task_runner,
+                                 const OptionalQuotaSettingsCallback& callback,
+                                 base::Optional<QuotaSettings> settings) {
+  task_runner->PostTask(FROM_HERE, base::Bind(callback, std::move(settings)));
+}
+}  // namespace
 
-  if (eviction_disabled_)
+void QuotaManager::GetQuotaSettings(const QuotaSettingsCallback& callback) {
+  if (base::TimeTicks::Now() - settings_timestamp_ <
+      settings_.refresh_interval) {
+    callback.Run(settings_);
+    return;
+  }
+
+  if (!settings_callbacks_.Add(callback))
     return;
 
-  std::set<GURL>* origins = new std::set<GURL>;
-  temporary_usage_tracker_->GetCachedOrigins(origins);
-  // This will call the StartEviction() when initial origin registration
-  // is completed.
-  PostTaskAndReplyWithResultForDBThread(
+  // We invoke our clients GetQuotaSettingsFunc on the
+  // UI thread and plumb the resulting value back to this thread.
+  get_settings_task_runner_->PostTask(
       FROM_HERE,
-      base::Bind(&InitializeTemporaryOriginsInfoOnDBThread,
-                 base::Owned(origins)),
-      base::Bind(&QuotaManager::DidInitializeTemporaryOriginsInfo,
+      base::Bind(
+          get_settings_function_,
+          base::Bind(
+              &DidGetSettingsThreadAdapter,
+              base::RetainedRef(base::ThreadTaskRunnerHandle::Get()),
+              base::Bind(&QuotaManager::DidGetSettings,
+                         weak_factory_.GetWeakPtr(), base::TimeTicks::Now()))));
+}
+
+void QuotaManager::DidGetSettings(base::TimeTicks start_ticks,
+                                  base::Optional<QuotaSettings> settings) {
+  if (!settings) {
+    settings = settings_;
+    settings->refresh_interval = base::TimeDelta::FromMinutes(1);
+  }
+  SetQuotaSettings(*settings);
+  settings_callbacks_.Run(*settings);
+  UMA_HISTOGRAM_MBYTES("Quota.GlobalTemporaryPoolSize", settings->pool_size);
+  UMA_HISTOGRAM_LONG_TIMES("Quota.TimeToGetSettings",
+                           base::TimeTicks::Now() - start_ticks);
+  LOG_IF(WARNING, settings->pool_size == 0)
+      << "No storage quota provided in QuotaSettings.";
+}
+
+void QuotaManager::GetStorageCapacity(const StorageCapacityCallback& callback) {
+  if (!storage_capacity_callbacks_.Add(callback))
+    return;
+  if (is_incognito_) {
+    GetQuotaSettings(
+        base::Bind(&QuotaManager::ContinueIncognitoGetStorageCapacity,
+                   weak_factory_.GetWeakPtr()));
+    return;
+  }
+  base::PostTaskAndReplyWithResult(
+      db_thread_.get(), FROM_HERE,
+      base::Bind(&QuotaManager::CallGetVolumeInfo, get_volume_info_fn_,
+                 profile_path_),
+      base::Bind(&QuotaManager::DidGetStorageCapacity,
                  weak_factory_.GetWeakPtr()));
 }
 
-void QuotaManager::DidInitializeTemporaryOriginsInfo(bool success) {
-  DidDatabaseWork(success);
-  if (success)
-    StartEviction();
+void QuotaManager::ContinueIncognitoGetStorageCapacity(
+    const QuotaSettings& settings) {
+  int64_t current_usage =
+      GetUsageTracker(kStorageTypeTemporary)->GetCachedUsage();
+  current_usage += GetUsageTracker(kStorageTypePersistent)->GetCachedUsage();
+  int64_t available_space =
+      std::max(INT64_C(0), settings.pool_size - current_usage);
+  DidGetStorageCapacity(std::make_pair(settings.pool_size, available_space));
 }
 
-void QuotaManager::DidGetAvailableSpace(int64_t space) {
-  // crbug.com/349708
-  TRACE_EVENT1("io", "QuotaManager::DidGetAvailableSpace",
-               "n_callbacks", available_space_callbacks_.size());
-
-  available_space_callbacks_.Run(kQuotaStatusOk, space);
+void QuotaManager::DidGetStorageCapacity(
+    const std::pair<int64_t, int64_t>& total_and_available) {
+  storage_capacity_callbacks_.Run(total_and_available.first,
+                                  total_and_available.second);
 }
 
 void QuotaManager::DidDatabaseWork(bool success) {
@@ -1824,41 +1638,30 @@
 }
 
 // static
-int64_t QuotaManager::CallGetAmountOfFreeDiskSpace(
+std::pair<int64_t, int64_t> QuotaManager::CallGetVolumeInfo(
     GetVolumeInfoFn get_volume_info_fn,
-    const base::FilePath& profile_path) {
+    const base::FilePath& path) {
   // crbug.com/349708
-  TRACE_EVENT0("io", "CallSystemGetAmountOfFreeDiskSpace");
-  if (!base::CreateDirectory(profile_path)) {
-    LOG(WARNING) << "Create directory failed for path" << profile_path.value();
-    return 0;
+  TRACE_EVENT0("io", "CallGetVolumeInfo");
+  if (!base::CreateDirectory(path)) {
+    LOG(WARNING) << "Create directory failed for path" << path.value();
+    return std::make_pair<int64_t, int64_t>(0, 0);
   }
-  uint64_t available, total;
-  if (!get_volume_info_fn(profile_path, &available, &total)) {
-    return 0;
+  std::pair<int64_t, int64_t> total_and_available = get_volume_info_fn(path);
+  if (total_and_available.first < 0 || total_and_available.second < 0) {
+    LOG(WARNING) << "Unable to get volume info: " << path.value();
+    return std::make_pair<int64_t, int64_t>(0, 0);
   }
-  UMA_HISTOGRAM_MBYTES("Quota.AvailableDiskSpace", available);
-  UMA_HISTOGRAM_MBYTES("Quota.TotalDiskSpace", total);
-  return static_cast<int64_t>(available);
+  UMA_HISTOGRAM_MBYTES("Quota.TotalDiskSpace", total_and_available.first);
+  UMA_HISTOGRAM_MBYTES("Quota.AvailableDiskSpace", total_and_available.second);
+  return total_and_available;
 }
 
-//static
-bool QuotaManager::GetVolumeInfo(const base::FilePath& path,
-                                 uint64_t* available_space,
-                                 uint64_t* total_size) {
-  // Inspired by similar code in the base::SysInfo class.
-  base::ThreadRestrictions::AssertIOAllowed();
-
-  int64_t available = base::SysInfo::AmountOfFreeDiskSpace(path);
-  if (available < 0)
-    return false;
-  int64_t total = base::SysInfo::AmountOfTotalDiskSpace(path);
-  if (total < 0)
-    return false;
-
-  *available_space = static_cast<uint64_t>(available);
-  *total_size = static_cast<uint64_t>(total);
-  return true;
+// static
+std::pair<int64_t, int64_t> QuotaManager::GetVolumeInfo(
+    const base::FilePath& path) {
+  return std::make_pair(base::SysInfo::AmountOfTotalDiskSpace(path),
+                        base::SysInfo::AmountOfFreeDiskSpace(path));
 }
 
 }  // namespace storage
diff --git a/storage/browser/quota/quota_manager.h b/storage/browser/quota/quota_manager.h
index a1ef11d..ded960e2 100644
--- a/storage/browser/quota/quota_manager.h
+++ b/storage/browser/quota/quota_manager.h
@@ -21,17 +21,17 @@
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
 #include "base/memory/weak_ptr.h"
+#include "base/optional.h"
 #include "base/sequenced_task_runner_helpers.h"
 #include "storage/browser/quota/quota_callbacks.h"
 #include "storage/browser/quota/quota_client.h"
 #include "storage/browser/quota/quota_database.h"
+#include "storage/browser/quota/quota_settings.h"
 #include "storage/browser/quota/quota_task.h"
 #include "storage/browser/quota/special_storage_policy.h"
 #include "storage/browser/quota/storage_observer.h"
 #include "storage/browser/storage_browser_export.h"
 
-class SiteEngagementEvictionPolicyWithQuotaManagerTest;
-
 namespace base {
 class FilePath;
 class SequencedTaskRunner;
@@ -60,44 +60,21 @@
 
 struct QuotaManagerDeleter;
 
-struct STORAGE_EXPORT UsageAndQuota {
-  int64_t usage;
-  int64_t global_limited_usage;
-  int64_t quota;
-  int64_t available_disk_space;
-
-  UsageAndQuota();
-  UsageAndQuota(int64_t usage,
-                int64_t global_limited_usage,
-                int64_t quota,
-                int64_t available_disk_space);
-};
-
-// TODO(calamity): Use this in the temporary storage eviction path.
-// An interface for deciding which origin's storage should be evicted when the
-// quota is exceeded.
-class STORAGE_EXPORT QuotaEvictionPolicy {
- public:
-  virtual ~QuotaEvictionPolicy() {}
-
-  // Returns the next origin to evict.  It might return an empty GURL when there
-  // are no evictable origins.
-  virtual void GetEvictionOrigin(
-      const scoped_refptr<SpecialStoragePolicy>& special_storage_policy,
-      const std::set<GURL>& exceptions,
-      const std::map<GURL, int64_t>& usage_map,
-      int64_t global_quota,
-      const GetOriginCallback& callback) = 0;
-};
-
 // An interface called by QuotaTemporaryStorageEvictor.
 class STORAGE_EXPORT QuotaEvictionHandler {
  public:
-  using EvictOriginDataCallback = StatusCallback;
-  using UsageAndQuotaCallback = base::Callback<
-      void(QuotaStatusCode status, const UsageAndQuota& usage_and_quota)>;
-  using VolumeInfoCallback = base::Callback<
-      void(bool success, uint64_t available_space, uint64_t total_space)>;
+  using EvictionRoundInfoCallback =
+      base::Callback<void(QuotaStatusCode status,
+                          const QuotaSettings& settings,
+                          int64_t available_space,
+                          int64_t total_space,
+                          int64_t global_usage,
+                          bool global_usage_is_complete)>;
+
+  // Called at the beginning of an eviction round to gather the info about
+  // the current settings, capacity, and usage.
+  virtual void GetEvictionRoundInfo(
+      const EvictionRoundInfoCallback& callback) = 0;
 
   // Returns next origin to evict.  It might return an empty GURL when there are
   // no evictable origins.
@@ -106,14 +83,10 @@
                                  int64_t global_quota,
                                  const GetOriginCallback& callback) = 0;
 
-  virtual void EvictOriginData(
-      const GURL& origin,
-      StorageType type,
-      const EvictOriginDataCallback& callback) = 0;
-
-  virtual void AsyncGetVolumeInfo(const VolumeInfoCallback& callback) = 0;
-  virtual void GetUsageAndQuotaForEviction(
-      const UsageAndQuotaCallback& callback) = 0;
+  // Called to evict an origin.
+  virtual void EvictOriginData(const GURL& origin,
+                               StorageType type,
+                               const StatusCallback& callback) = 0;
 
  protected:
   virtual ~QuotaEvictionHandler() {}
@@ -135,11 +108,10 @@
       public QuotaEvictionHandler,
       public base::RefCountedThreadSafe<QuotaManager, QuotaManagerDeleter> {
  public:
-  typedef base::Callback<void(QuotaStatusCode,
-                              int64_t /* usage */,
-                              int64_t /* quota */)> GetUsageAndQuotaCallback;
+  typedef base::Callback<
+      void(QuotaStatusCode, int64_t /* usage */, int64_t /* quota */)>
+      UsageAndQuotaCallback;
 
-  static const int64_t kIncognitoDefaultQuotaLimit;
   static const int64_t kNoLimit;
 
   QuotaManager(
@@ -147,7 +119,11 @@
       const base::FilePath& profile_path,
       const scoped_refptr<base::SingleThreadTaskRunner>& io_thread,
       const scoped_refptr<base::SequencedTaskRunner>& db_thread,
-      const scoped_refptr<SpecialStoragePolicy>& special_storage_policy);
+      const scoped_refptr<SpecialStoragePolicy>& special_storage_policy,
+      const GetQuotaSettingsFunc& get_settings_function);
+
+  const QuotaSettings& settings() const { return settings_; }
+  void SetQuotaSettings(const QuotaSettings& settings);
 
   // Returns a proxy object that can be used on any thread.
   QuotaManagerProxy* proxy() { return proxy_.get(); }
@@ -160,7 +136,7 @@
   virtual void GetUsageAndQuotaForWebApps(
       const GURL& origin,
       StorageType type,
-      const GetUsageAndQuotaCallback& callback);
+      const UsageAndQuotaCallback& callback);
 
   // Called by StorageClients.
   // This method is declared as virtual to allow test code to override it.
@@ -168,10 +144,9 @@
   // For UnlimitedStorage origins, this version skips usage and quota handling
   // to avoid extra query cost.
   // Do not call this method for apps/user-facing code.
-  virtual void GetUsageAndQuota(
-      const GURL& origin,
-      StorageType type,
-      const GetUsageAndQuotaCallback& callback);
+  virtual void GetUsageAndQuota(const GURL& origin,
+                                StorageType type,
+                                const UsageAndQuotaCallback& callback);
 
   // Called by clients via proxy.
   // Client storage should call this method when storage is accessed.
@@ -202,10 +177,6 @@
                             StorageType type,
                             bool enabled);
 
-  // Set the eviction policy to use when choosing an origin to evict.
-  void SetTemporaryStorageEvictionPolicy(
-      std::unique_ptr<QuotaEvictionPolicy> policy);
-
   // DeleteOriginData and DeleteHostData (surprisingly enough) delete data of a
   // particular StorageType associated with either a specific origin or set of
   // origins. Each method additionally requires a |quota_client_mask| which
@@ -223,13 +194,6 @@
                       const StatusCallback& callback);
 
   // Called by UI and internal modules.
-  void GetAvailableSpace(const AvailableSpaceCallback& callback);
-  void GetTemporaryGlobalQuota(const QuotaCallback& callback);
-
-  // Ok to call with NULL callback.
-  void SetTemporaryGlobalOverrideQuota(int64_t new_quota,
-                                       const QuotaCallback& callback);
-
   void GetPersistentHostQuota(const std::string& host,
                               const QuotaCallback& callback);
   void SetPersistentHostQuota(const std::string& host,
@@ -248,11 +212,6 @@
 
   bool IsStorageUnlimited(const GURL& origin, StorageType type) const;
 
-  bool CanQueryDiskSize(const GURL& origin) const {
-    return special_storage_policy_.get() &&
-           special_storage_policy_->CanQueryDiskSize(origin);
-  }
-
   virtual void GetOriginsModifiedSince(StorageType type,
                                        base::Time modified_since,
                                        const GetOriginsCallback& callback);
@@ -266,26 +225,17 @@
   void RemoveStorageObserverForFilter(StorageObserver* observer,
                                       const StorageObserver::Filter& filter);
 
-  // Determines the portion of the temp pool that can be
-  // utilized by a single host (ie. 5 for 20%).
-  static const int kPerHostTemporaryPortion;
-
   static const int64_t kPerHostPersistentQuotaLimit;
-
   static const char kDatabaseName[];
-
   static const int kThresholdOfErrorsToBeBlacklisted;
-
   static const int kEvictionIntervalInMilliSeconds;
-
   static const char kTimeBetweenRepeatedOriginEvictionsHistogram[];
   static const char kEvictedOriginAccessedCountHistogram[];
   static const char kEvictedOriginTimeSinceAccessHistogram[];
 
-  // These are kept non-const so that test code can change the value.
+  // Kept non-const so that test code can change the value.
   // TODO(kinuko): Make this a real const value and add a proper way to set
   // the quota for syncable storage. (http://crbug.com/155488)
-  static int64_t kMinimumPreserveForSystem;
   static int64_t kSyncableStorageDefaultHostQuota;
 
  protected:
@@ -302,13 +252,12 @@
   friend class QuotaManagerProxy;
   friend class QuotaTemporaryStorageEvictor;
   friend struct QuotaManagerDeleter;
-  friend class ::SiteEngagementEvictionPolicyWithQuotaManagerTest;
 
+  class EvictionRoundInfoHelper;
+  class UsageAndQuotaHelper;
   class GetUsageInfoTask;
-
   class OriginDataDeleter;
   class HostDataDeleter;
-
   class GetModifiedSinceHelper;
   class DumpQuotaTableHelper;
   class DumpOriginInfoTableHelper;
@@ -318,10 +267,13 @@
   typedef std::vector<QuotaTableEntry> QuotaTableEntries;
   typedef std::vector<OriginInfoTableEntry> OriginInfoTableEntries;
 
+  using QuotaSettingsCallback = base::Callback<void(const QuotaSettings&)>;
+
   // Function pointer type used to store the function which returns
   // information about the volume containing the given FilePath.
-  using GetVolumeInfoFn = bool(*)(const base::FilePath&,
-                                  uint64_t* available, uint64_t* total);
+  // The value returned is std::pair<total_space, available_space>.
+  using GetVolumeInfoFn =
+      std::pair<int64_t, int64_t> (*)(const base::FilePath&);
 
   typedef base::Callback<void(const QuotaTableEntries&)>
       DumpQuotaTableCallback;
@@ -329,28 +281,36 @@
       DumpOriginInfoTableCallback;
 
   typedef CallbackQueue<base::Closure> ClosureQueue;
-  typedef CallbackQueue<AvailableSpaceCallback, QuotaStatusCode, int64_t>
-      AvailableSpaceCallbackQueue;
   typedef CallbackQueueMap<QuotaCallback, std::string, QuotaStatusCode, int64_t>
       HostQuotaCallbackMap;
+  using QuotaSettingsCallbackQueue =
+      CallbackQueue<QuotaSettingsCallback, const QuotaSettings&>;
+
+  // The values returned total_space, available_space.
+  using StorageCapacityCallback = base::Callback<void(int64_t, int64_t)>;
+  using StorageCapacityCallbackQueue =
+      CallbackQueue<StorageCapacityCallback, int64_t, int64_t>;
 
   struct EvictionContext {
     EvictionContext();
-    virtual ~EvictionContext();
+    ~EvictionContext();
     GURL evicted_origin;
     StorageType evicted_type;
-
-    EvictOriginDataCallback evict_origin_data_callback;
+    StatusCallback evict_origin_data_callback;
   };
 
-  typedef QuotaEvictionHandler::UsageAndQuotaCallback
-      UsageAndQuotaDispatcherCallback;
-
   // This initialization method is lazily called on the IO thread
   // when the first quota manager API is called.
   // Initialize must be called after all quota clients are added to the
   // manager by RegisterStorage.
   void LazyInitialize();
+  void FinishLazyInitialize(bool is_database_bootstraped);
+  void BootstrapDatabaseForEviction(
+      const GetOriginCallback& did_get_origin_callback,
+      int64_t unused_usage,
+      int64_t unused_unlimited_usage);
+  void DidBootstrapDatabase(const GetOriginCallback& did_get_origin_callback,
+                            bool success);
 
   // Called by clients via proxy.
   // Registers a quota client to the manager.
@@ -412,20 +372,11 @@
                          const GetOriginCallback& callback) override;
   void EvictOriginData(const GURL& origin,
                        StorageType type,
-                       const EvictOriginDataCallback& callback) override;
-  void GetUsageAndQuotaForEviction(
-      const UsageAndQuotaCallback& callback) override;
-  void AsyncGetVolumeInfo(const VolumeInfoCallback& callback) override;
-
-  void DidGetVolumeInfo(
-      const VolumeInfoCallback& callback,
-      uint64_t* available_space, uint64_t* total_space, bool success);
+                       const StatusCallback& callback) override;
+  void GetEvictionRoundInfo(const EvictionRoundInfoCallback& callback) override;
 
   void GetLRUOrigin(StorageType type, const GetOriginCallback& callback);
 
-  void DidSetTemporaryGlobalOverrideQuota(const QuotaCallback& callback,
-                                          const int64_t* new_quota,
-                                          bool success);
   void DidGetPersistentHostQuota(const std::string& host,
                                  const int64_t* quota,
                                  bool success);
@@ -433,16 +384,16 @@
                                  const QuotaCallback& callback,
                                  const int64_t* new_quota,
                                  bool success);
-  void DidInitialize(int64_t* temporary_quota_override,
-                     int64_t* desired_available_space,
-                     bool success);
   void DidGetLRUOrigin(const GURL* origin,
                        bool success);
-  void DidGetInitialTemporaryGlobalQuota(base::TimeTicks start_ticks,
-                                         QuotaStatusCode status,
-                                         int64_t quota_unused);
-  void DidInitializeTemporaryOriginsInfo(bool success);
-  void DidGetAvailableSpace(int64_t space);
+  void GetQuotaSettings(const QuotaSettingsCallback& callback);
+  void DidGetSettings(base::TimeTicks start_ticks,
+                      base::Optional<QuotaSettings> settings);
+  void GetStorageCapacity(const StorageCapacityCallback& callback);
+  void ContinueIncognitoGetStorageCapacity(const QuotaSettings& settings);
+  void DidGetStorageCapacity(
+      const std::pair<int64_t, int64_t>& total_and_available);
+
   void DidDatabaseWork(bool success);
 
   void DeleteOnCorrectThread() const;
@@ -452,12 +403,10 @@
       const base::Callback<bool(QuotaDatabase*)>& task,
       const base::Callback<void(bool)>& reply);
 
-  static int64_t CallGetAmountOfFreeDiskSpace(
-      GetVolumeInfoFn get_vol_info_fn,
-      const base::FilePath& profile_path);
-  static bool GetVolumeInfo(const base::FilePath& path,
-                            uint64_t* available_space,
-                            uint64_t* total_size);
+  static std::pair<int64_t, int64_t> CallGetVolumeInfo(
+      GetVolumeInfoFn get_volume_info_fn,
+      const base::FilePath& path);
+  static std::pair<int64_t, int64_t> GetVolumeInfo(const base::FilePath& path);
 
   const bool is_incognito_;
   const base::FilePath profile_path_;
@@ -468,6 +417,14 @@
   scoped_refptr<base::SingleThreadTaskRunner> io_thread_;
   scoped_refptr<base::SequencedTaskRunner> db_thread_;
   mutable std::unique_ptr<QuotaDatabase> database_;
+  bool is_database_bootstrapped_ = false;
+
+  GetQuotaSettingsFunc get_settings_function_;
+  scoped_refptr<base::TaskRunner> get_settings_task_runner_;
+  QuotaSettings settings_;
+  base::TimeTicks settings_timestamp_;
+  QuotaSettingsCallbackQueue settings_callbacks_;
+  StorageCapacityCallbackQueue storage_capacity_callbacks_;
 
   GetOriginCallback lru_origin_callback_;
   std::set<GURL> access_notified_origins_;
@@ -482,17 +439,10 @@
 
   std::unique_ptr<QuotaTemporaryStorageEvictor> temporary_storage_evictor_;
   EvictionContext eviction_context_;
-  std::unique_ptr<QuotaEvictionPolicy> temporary_storage_eviction_policy_;
   bool is_getting_eviction_origin_;
 
-  ClosureQueue db_initialization_callbacks_;
-  AvailableSpaceCallbackQueue available_space_callbacks_;
   HostQuotaCallbackMap persistent_host_quota_callbacks_;
 
-  bool temporary_quota_initialized_;
-  int64_t temporary_quota_override_;
-  int64_t desired_available_space_;
-
   // Map from origin to count.
   std::map<GURL, int> origins_in_use_;
   // Map from origin to error count.
diff --git a/storage/browser/quota/quota_manager_proxy.cc b/storage/browser/quota/quota_manager_proxy.cc
index 0c1ad0a..b67cfb9c 100644
--- a/storage/browser/quota/quota_manager_proxy.cc
+++ b/storage/browser/quota/quota_manager_proxy.cc
@@ -20,7 +20,7 @@
 
 void DidGetUsageAndQuota(
     base::SequencedTaskRunner* original_task_runner,
-    const QuotaManagerProxy::GetUsageAndQuotaCallback& callback,
+    const QuotaManagerProxy::UsageAndQuotaCallback& callback,
     QuotaStatusCode status,
     int64_t usage,
     int64_t quota) {
@@ -130,7 +130,7 @@
     base::SequencedTaskRunner* original_task_runner,
     const GURL& origin,
     StorageType type,
-    const GetUsageAndQuotaCallback& callback) {
+    const UsageAndQuotaCallback& callback) {
   if (!io_thread_->BelongsToCurrentThread()) {
     io_thread_->PostTask(
         FROM_HERE, base::Bind(&QuotaManagerProxy::GetUsageAndQuota, this,
diff --git a/storage/browser/quota/quota_manager_proxy.h b/storage/browser/quota/quota_manager_proxy.h
index 19a1f89d..7125a6a 100644
--- a/storage/browser/quota/quota_manager_proxy.h
+++ b/storage/browser/quota/quota_manager_proxy.h
@@ -34,8 +34,7 @@
 class STORAGE_EXPORT QuotaManagerProxy
     : public base::RefCountedThreadSafe<QuotaManagerProxy> {
  public:
-  typedef QuotaManager::GetUsageAndQuotaCallback
-      GetUsageAndQuotaCallback;
+  typedef QuotaManager::UsageAndQuotaCallback UsageAndQuotaCallback;
 
   virtual void RegisterClient(QuotaClient* client);
   virtual void NotifyStorageAccessed(QuotaClient::ID client_id,
@@ -52,11 +51,10 @@
                                     const GURL& origin,
                                     StorageType type,
                                     bool enabled);
-  virtual void GetUsageAndQuota(
-      base::SequencedTaskRunner* original_task_runner,
-      const GURL& origin,
-      StorageType type,
-      const GetUsageAndQuotaCallback& callback);
+  virtual void GetUsageAndQuota(base::SequencedTaskRunner* original_task_runner,
+                                const GURL& origin,
+                                StorageType type,
+                                const UsageAndQuotaCallback& callback);
 
   // This method may only be called on the IO thread.
   // It may return NULL if the manager has already been deleted.
diff --git a/storage/browser/quota/quota_settings.cc b/storage/browser/quota/quota_settings.cc
new file mode 100644
index 0000000..2b737098
--- /dev/null
+++ b/storage/browser/quota/quota_settings.cc
@@ -0,0 +1,83 @@
+// 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.
+
+#include "storage/browser/quota/quota_settings.h"
+
+#include <algorithm>
+
+#include "base/metrics/histogram_macros.h"
+#include "base/sys_info.h"
+
+#define UMA_HISTOGRAM_MBYTES(name, sample)                                     \
+  UMA_HISTOGRAM_CUSTOM_COUNTS((name), static_cast<int>((sample) / kMBytes), 1, \
+                              10 * 1024 * 1024 /* 10TB */, 100)
+
+namespace storage {
+
+base::Optional<storage::QuotaSettings> CalculateNominalDynamicSettings(
+    const base::FilePath& partition_path,
+    bool is_incognito) {
+  const int64_t kMBytes = 1024 * 1024;
+
+  if (is_incognito) {
+    storage::QuotaSettings settings;
+    settings.pool_size =
+        std::min(300 * kMBytes, base::SysInfo::AmountOfPhysicalMemory() / 10);
+    settings.per_host_quota = settings.pool_size / 3;
+    settings.refresh_interval = base::TimeDelta::Max();
+    return settings;
+  }
+
+  // The fraction of the device's storage the browser is willing to
+  // use for temporary storage, this is applied after adjusting the
+  // total to take os_accomodation into account.
+  const double kTemporaryPoolSizeRatio = 1.0 / 3.0;  // 33%
+
+  // The fraction of the device's storage the browser attempts to
+  // keep free.
+  const double kMustRemainAvailableRatio = 0.1;
+
+  // Determines the portion of the temp pool that can be
+  // utilized by a single host (ie. 5 for 20%).
+  const int kPerHostTemporaryPortion = 5;
+
+  // os_accomodation is an estimate of how much storage is needed for
+  // the os and essential application code outside of the browser.
+  const int64_t kDefaultOSAccomodation =
+#if defined(OS_ANDROID)
+      1000 * kMBytes;
+#elif defined(OS_CHROMEOS)
+      1000 * kMBytes;
+#elif defined(OS_WIN) || defined(OS_LINUX) || defined(OS_MACOSX)
+      10000 * kMBytes;
+#else
+#error "Port: Need to define an OS accomodation value for unknown OS."
+#endif
+
+  storage::QuotaSettings settings;
+
+  int64_t total = base::SysInfo::AmountOfTotalDiskSpace(partition_path);
+  if (total == -1) {
+    LOG(ERROR) << "Unable to compute QuotaSettings.";
+    return base::nullopt;
+  }
+
+  // If our hardcoded OS accomodation is too large for the volume size, define
+  // the value as a fraction of the total volume size instead.
+  int64_t os_accomodation =
+      std::min(kDefaultOSAccomodation, static_cast<int64_t>(total * 0.8));
+  UMA_HISTOGRAM_MBYTES("Quota.OSAccomodationDelta",
+                       kDefaultOSAccomodation - os_accomodation);
+
+  int64_t adjusted_total = total - os_accomodation;
+  int64_t pool_size = adjusted_total * kTemporaryPoolSizeRatio;
+
+  settings.pool_size = pool_size;
+  settings.must_remain_available = total * kMustRemainAvailableRatio;
+  settings.per_host_quota = pool_size / kPerHostTemporaryPortion;
+  settings.refresh_interval = base::TimeDelta::FromSeconds(60);
+  return settings;
+}
+
+}  // namespace
diff --git a/storage/browser/quota/quota_settings.h b/storage/browser/quota/quota_settings.h
new file mode 100644
index 0000000..e05d5530
--- /dev/null
+++ b/storage/browser/quota/quota_settings.h
@@ -0,0 +1,82 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef STORAGE_BROWSER_QUOTA_QUOTA_SETTINGS_H_
+#define STORAGE_BROWSER_QUOTA_QUOTA_SETTINGS_H_
+
+#include <stdint.h>
+
+#include "base/callback.h"
+#include "base/files/file_path.h"
+#include "base/optional.h"
+#include "base/time/time.h"
+#include "storage/browser/storage_browser_export.h"
+
+namespace storage {
+
+// Settings the storage lib embedder must provide to the QuotaManager.
+struct QuotaSettings {
+  QuotaSettings() = default;
+  QuotaSettings(int64_t pool_size,
+                int64_t per_host_quota,
+                int64_t must_remain_available)
+      : pool_size(pool_size),
+        per_host_quota(per_host_quota),
+        must_remain_available(must_remain_available) {}
+
+  // The target size in bytes of the shared pool of disk space the quota
+  // system allows for use by websites using HTML5 storage apis, for
+  // example an embedder may use 50% of the total volume size.
+  int64_t pool_size = 0;
+
+  // The amount in bytes of the pool an individual site may consume. The
+  // value must be less than or equal to the pool_size.
+  int64_t per_host_quota = 0;
+
+  // The amount of space that must remain available on the storage
+  // volume. As the volume approaches this limit, the quota system gets
+  // more aggressive about evicting data and disallowing new data.
+  int64_t must_remain_available = 0;
+
+  // The quota system querries the embedder for the QuataSettings,
+  // but will rate limit the frequency of the querries to no more than once
+  // per refresh interval.
+  base::TimeDelta refresh_interval = base::TimeDelta::Max();
+};
+
+// Function type used to return the settings in response to a
+// GetQuotaSettingsFunc invocation. If the embedder cannot
+// produce a settings values, base::nullopt can be returned.
+using OptionalQuotaSettingsCallback =
+    base::Callback<void(base::Optional<QuotaSettings>)>;
+
+// Function type used to query the embedder about the quota manager settings.
+// This function is invoked on the UI thread.
+using GetQuotaSettingsFunc =
+    base::Callback<void(const OptionalQuotaSettingsCallback& callback)>;
+
+// Returns settings based on the size of the volume containing the storage
+// partition and a guestimate of the size required for the OS. The refresh
+// interval is 60 seconds to accomodate changes to the size of the volume.
+// Except, in the case of incognito, the poolize and quota values are based
+// on the amount of physical memory and the rerfresh interval is max'd out.
+STORAGE_EXPORT
+base::Optional<storage::QuotaSettings> CalculateNominalDynamicSettings(
+    const base::FilePath& partition_path,
+    bool is_incognito);
+
+// Returns settings with a poolsize of zero and no per host quota.
+inline QuotaSettings GetNoQuotaSettings() {
+  return QuotaSettings();
+}
+
+// Returns settings that provide given |per_host_quota| and a total poolsize of
+// five times that.
+inline QuotaSettings GetHardCodedSettings(int64_t per_host_quota) {
+  return QuotaSettings(per_host_quota * 5, per_host_quota, per_host_quota);
+}
+
+}  // namespace storage
+
+#endif  // STORAGE_BROWSER_QUOTA_QUOTA_MANAGER_H_
diff --git a/storage/browser/quota/quota_temporary_storage_evictor.cc b/storage/browser/quota/quota_temporary_storage_evictor.cc
index 6cce670..1a6c0257 100644
--- a/storage/browser/quota/quota_temporary_storage_evictor.cc
+++ b/storage/browser/quota/quota_temporary_storage_evictor.cc
@@ -8,6 +8,7 @@
 
 #include <algorithm>
 
+#include "base/auto_reset.h"
 #include "base/bind.h"
 #include "base/metrics/histogram_macros.h"
 #include "storage/browser/quota/quota_manager.h"
@@ -29,16 +30,12 @@
 const double kUsageRatioToStartEviction = 0.7;
 const int kThresholdOfErrorsToStopEviction = 5;
 const int kHistogramReportIntervalMinutes = 60;
-const double kMustRemainAvailableRatio = 0.1;
-const int64_t kDefaultMustRemainAvailableSpace = 1024 * kMBytes;
+
 const double kDiskSpaceShortageAllowanceRatio = 0.5;
 }
 
 namespace storage {
 
-const int QuotaTemporaryStorageEvictor::
-    kMinAvailableToStartEvictionNotSpecified = -1;
-
 QuotaTemporaryStorageEvictor::EvictionRoundStatistics::EvictionRoundStatistics()
     : in_round(false),
       is_initialized(false),
@@ -52,11 +49,9 @@
 QuotaTemporaryStorageEvictor::QuotaTemporaryStorageEvictor(
     QuotaEvictionHandler* quota_eviction_handler,
     int64_t interval_ms)
-    : min_available_to_start_eviction_(
-          kMinAvailableToStartEvictionNotSpecified),
-      quota_eviction_handler_(quota_eviction_handler),
+    : quota_eviction_handler_(quota_eviction_handler),
       interval_ms_(interval_ms),
-      repeated_eviction_(true),
+      timer_disabled_for_testing_(false),
       weak_factory_(this) {
   DCHECK(quota_eviction_handler);
 }
@@ -142,6 +137,8 @@
 
 void QuotaTemporaryStorageEvictor::Start() {
   DCHECK(CalledOnValidThread());
+
+  base::AutoReset<bool> auto_reset(&timer_disabled_for_testing_, false);
   StartEvictionTimerWithDelay(0);
 
   if (histogram_timer_.IsRunning())
@@ -153,7 +150,7 @@
 }
 
 void QuotaTemporaryStorageEvictor::StartEvictionTimerWithDelay(int delay_ms) {
-  if (eviction_timer_.IsRunning())
+  if (eviction_timer_.IsRunning() || timer_disabled_for_testing_)
     return;
   eviction_timer_.Start(FROM_HERE, base::TimeDelta::FromMilliseconds(delay_ms),
                         this, &QuotaTemporaryStorageEvictor::ConsiderEviction);
@@ -161,66 +158,49 @@
 
 void QuotaTemporaryStorageEvictor::ConsiderEviction() {
   OnEvictionRoundStarted();
-
-  if (min_available_to_start_eviction_ ==
-      kMinAvailableToStartEvictionNotSpecified) {
-    quota_eviction_handler_->AsyncGetVolumeInfo(
-        base::Bind(&QuotaTemporaryStorageEvictor::OnGotVolumeInfo,
-                   weak_factory_.GetWeakPtr()));
-  } else {
-    quota_eviction_handler_->GetUsageAndQuotaForEviction(
-        base::Bind(&QuotaTemporaryStorageEvictor::OnGotUsageAndQuotaForEviction,
-                   weak_factory_.GetWeakPtr(),
-                   min_available_to_start_eviction_));
-  }
+  quota_eviction_handler_->GetEvictionRoundInfo(
+      base::Bind(&QuotaTemporaryStorageEvictor::OnGotEvictionRoundInfo,
+                 weak_factory_.GetWeakPtr()));
 }
 
-void QuotaTemporaryStorageEvictor::OnGotVolumeInfo(
-    bool success, uint64_t available_space, uint64_t total_size) {
-  // Compute how much to keep free as a function of total disk size.
-  int64_t must_remain_available_space = success ?
-      static_cast<int64_t>(total_size * kMustRemainAvailableRatio) :
-      kDefaultMustRemainAvailableSpace;
-
-  quota_eviction_handler_->GetUsageAndQuotaForEviction(
-      base::Bind(&QuotaTemporaryStorageEvictor::OnGotUsageAndQuotaForEviction,
-                 weak_factory_.GetWeakPtr(), must_remain_available_space));
-}
-
-void QuotaTemporaryStorageEvictor::OnGotUsageAndQuotaForEviction(
-    int64_t must_remain_available_space,
+void QuotaTemporaryStorageEvictor::OnGotEvictionRoundInfo(
     QuotaStatusCode status,
-    const UsageAndQuota& qau) {
-  DCHECK(CalledOnValidThread());
+    const QuotaSettings& settings,
+    int64_t available_space,
+    int64_t total_space,
+    int64_t current_usage,
+    bool current_usage_is_complete) {
+  DCHECK_GE(current_usage, 0);
 
-  int64_t usage = qau.global_limited_usage;
-  DCHECK_GE(usage, 0);
+  // Note: if there is no storage pressure, |current_usage|
+  // may not be fully calculated and may be 0.
 
   if (status != kQuotaStatusOk)
     ++statistics_.num_errors_on_getting_usage_and_quota;
 
   int64_t usage_overage = std::max(
       static_cast<int64_t>(0),
-      usage - static_cast<int64_t>(qau.quota * kUsageRatioToStartEviction));
-
-  int64_t diskspace_shortage = std::max(
-      static_cast<int64_t>(0),
-      must_remain_available_space - qau.available_disk_space);
+      current_usage - static_cast<int64_t>(settings.pool_size *
+                                           kUsageRatioToStartEviction));
+  int64_t diskspace_shortage =
+      std::max(static_cast<int64_t>(0),
+               settings.must_remain_available - available_space);
+  DCHECK(current_usage_is_complete || diskspace_shortage == 0);
 
   // If we're using so little that freeing all of it wouldn't help,
   // don't let the low space condition cause us to delete it all.
-  if (usage < static_cast<int64_t>(diskspace_shortage *
-                                   kDiskSpaceShortageAllowanceRatio)) {
+  if (current_usage < static_cast<int64_t>(diskspace_shortage *
+                                           kDiskSpaceShortageAllowanceRatio)) {
     diskspace_shortage = 0;
   }
 
   if (!round_statistics_.is_initialized) {
     round_statistics_.usage_overage_at_round = usage_overage;
     round_statistics_.diskspace_shortage_at_round = diskspace_shortage;
-    round_statistics_.usage_on_beginning_of_round = usage;
+    round_statistics_.usage_on_beginning_of_round = current_usage;
     round_statistics_.is_initialized = true;
   }
-  round_statistics_.usage_on_end_of_round = usage;
+  round_statistics_.usage_on_end_of_round = current_usage;
 
   int64_t amount_to_evict = std::max(usage_overage, diskspace_shortage);
   if (status == kQuotaStatusOk && amount_to_evict > 0) {
@@ -228,33 +208,31 @@
     // TODO(michaeln): if the reason for eviction is low physical disk space,
     // make 'unlimited' origins subject to eviction too.
     quota_eviction_handler_->GetEvictionOrigin(
-        kStorageTypeTemporary, in_progress_eviction_origins_, qau.quota,
+        kStorageTypeTemporary, in_progress_eviction_origins_,
+        settings.pool_size,
         base::Bind(&QuotaTemporaryStorageEvictor::OnGotEvictionOrigin,
                    weak_factory_.GetWeakPtr()));
-  } else {
-    if (repeated_eviction_) {
-      // No action required, sleep for a while and check again later.
-      if (statistics_.num_errors_on_getting_usage_and_quota <
-          kThresholdOfErrorsToStopEviction) {
-        StartEvictionTimerWithDelay(interval_ms_);
-      } else {
-        // TODO(dmikurube): Try restarting eviction after a while.
-        LOG(WARNING) << "Stopped eviction of temporary storage due to errors "
-                        "in GetUsageAndQuotaForEviction.";
-      }
-    }
-    OnEvictionRoundFinished();
+    return;
   }
 
-  // TODO(dmikurube): Add error handling for the case status != kQuotaStatusOk.
+  // No action required, sleep for a while and check again later.
+  if (statistics_.num_errors_on_getting_usage_and_quota <
+      kThresholdOfErrorsToStopEviction) {
+    StartEvictionTimerWithDelay(interval_ms_);
+  } else {
+    // TODO(dmikurube): Add error handling for the case status is not OK.
+    // TODO(dmikurube): Try restarting eviction after a while.
+    LOG(WARNING) << "Stopped eviction of temporary storage due to errors";
+  }
+
+  OnEvictionRoundFinished();
 }
 
 void QuotaTemporaryStorageEvictor::OnGotEvictionOrigin(const GURL& origin) {
   DCHECK(CalledOnValidThread());
 
   if (origin.is_empty()) {
-    if (repeated_eviction_)
-      StartEvictionTimerWithDelay(interval_ms_);
+    StartEvictionTimerWithDelay(interval_ms_);
     OnEvictionRoundFinished();
     return;
   }
@@ -284,10 +262,8 @@
     ConsiderEviction();
   } else {
     ++statistics_.num_errors_on_evicting_origin;
-    if (repeated_eviction_) {
-      // Sleep for a while and retry again until we see too many errors.
-      StartEvictionTimerWithDelay(interval_ms_);
-    }
+    // Sleep for a while and retry again until we see too many errors.
+    StartEvictionTimerWithDelay(interval_ms_);
     OnEvictionRoundFinished();
   }
 }
diff --git a/storage/browser/quota/quota_temporary_storage_evictor.h b/storage/browser/quota/quota_temporary_storage_evictor.h
index 8039426..187d3396 100644
--- a/storage/browser/quota/quota_temporary_storage_evictor.h
+++ b/storage/browser/quota/quota_temporary_storage_evictor.h
@@ -27,7 +27,7 @@
 namespace storage {
 
 class QuotaEvictionHandler;
-struct UsageAndQuota;
+struct QuotaSettings;
 
 class STORAGE_EXPORT QuotaTemporaryStorageEvictor : public base::NonThreadSafe {
  public:
@@ -78,41 +78,23 @@
   void ReportPerHourHistogram();
   void Start();
 
-  void reset_min_available_disk_space_to_start_eviction() {
-    min_available_to_start_eviction_ =
-        kMinAvailableToStartEvictionNotSpecified;
-  }
-  void set_min_available_disk_space_to_start_eviction(int64_t value) {
-    min_available_to_start_eviction_ = value;
-  }
-
  private:
   friend class content::QuotaTemporaryStorageEvictorTest;
 
   void StartEvictionTimerWithDelay(int delay_ms);
   void ConsiderEviction();
-  void OnGotVolumeInfo(bool success,
-                       uint64_t available_space,
-                       uint64_t total_size);
-  void OnGotUsageAndQuotaForEviction(
-      int64_t must_remain_available_space,
-      QuotaStatusCode status,
-      const UsageAndQuota& quota_and_usage);
+  void OnGotEvictionRoundInfo(QuotaStatusCode status,
+                              const QuotaSettings& settings,
+                              int64_t available_space,
+                              int64_t total_space,
+                              int64_t current_usage,
+                              bool current_usage_is_complete);
   void OnGotEvictionOrigin(const GURL& origin);
   void OnEvictionComplete(QuotaStatusCode status);
 
   void OnEvictionRoundStarted();
   void OnEvictionRoundFinished();
 
-  // This is only used for tests.
-  void set_repeated_eviction(bool repeated_eviction) {
-    repeated_eviction_ = repeated_eviction;
-  }
-
-  static const int kMinAvailableToStartEvictionNotSpecified;
-
-  int64_t min_available_to_start_eviction_;
-
   // Not owned; quota_eviction_handler owns us.
   QuotaEvictionHandler* quota_eviction_handler_;
 
@@ -124,7 +106,7 @@
   std::set<GURL> in_progress_eviction_origins_;
 
   int64_t interval_ms_;
-  bool repeated_eviction_;
+  bool timer_disabled_for_testing_;
 
   base::OneShotTimer eviction_timer_;
   base::RepeatingTimer histogram_timer_;
diff --git a/storage/browser/quota/special_storage_policy.h b/storage/browser/quota/special_storage_policy.h
index 5c97806a..cac9f94 100644
--- a/storage/browser/quota/special_storage_policy.h
+++ b/storage/browser/quota/special_storage_policy.h
@@ -51,10 +51,6 @@
   // Durable storage is not subject to storage pressure eviction.
   virtual bool IsStorageDurable(const GURL& origin) = 0;
 
-  // Some origins (e.g. installed apps) have access to the size of the remaining
-  // disk capacity.
-  virtual bool CanQueryDiskSize(const GURL& origin) = 0;
-
   // Checks if the origin contains per-site isolated storage.
   virtual bool HasIsolatedStorage(const GURL& origin) = 0;
 
diff --git a/storage/browser/quota/usage_tracker.cc b/storage/browser/quota/usage_tracker.cc
index 041f940..a29004e 100644
--- a/storage/browser/quota/usage_tracker.cc
+++ b/storage/browser/quota/usage_tracker.cc
@@ -136,6 +136,13 @@
   client_tracker->UpdateUsageCache(origin, delta);
 }
 
+int64_t UsageTracker::GetCachedUsage() const {
+  int64_t usage = 0;
+  for (const auto& client_id_and_tracker : client_tracker_map_)
+    usage += client_id_and_tracker.second->GetCachedUsage();
+  return usage;
+}
+
 void UsageTracker::GetCachedHostsUsage(
     std::map<std::string, int64_t>* host_usage) const {
   DCHECK(host_usage);
diff --git a/storage/browser/quota/usage_tracker.h b/storage/browser/quota/usage_tracker.h
index 074b4f54..343fab6 100644
--- a/storage/browser/quota/usage_tracker.h
+++ b/storage/browser/quota/usage_tracker.h
@@ -47,8 +47,9 @@
   void UpdateUsageCache(QuotaClient::ID client_id,
                         const GURL& origin,
                         int64_t delta);
-  void GetCachedOriginsUsage(std::map<GURL, int64_t>* origin_usage) const;
+  int64_t GetCachedUsage() const;
   void GetCachedHostsUsage(std::map<std::string, int64_t>* host_usage) const;
+  void GetCachedOriginsUsage(std::map<GURL, int64_t>* origin_usage) const;
   void GetCachedOrigins(std::set<GURL>* origins) const;
   bool IsWorking() const {
     return global_usage_callbacks_.HasCallbacks() ||
diff --git a/testing/buildbot/filters/browser-side-navigation.linux.browser_tests.filter b/testing/buildbot/filters/browser-side-navigation.linux.browser_tests.filter
index 34912ff..7cae62b5 100644
--- a/testing/buildbot/filters/browser-side-navigation.linux.browser_tests.filter
+++ b/testing/buildbot/filters/browser-side-navigation.linux.browser_tests.filter
@@ -1,5 +1,11 @@
 -ChromeServiceWorkerTest.FallbackMainResourceRequestWhenJSDisabled
 
+# NavigationHandle::IsRendererInitiated some times return differently in
+# browser side navigation tests.
+-SBNavigationObserverBrowserTest.NewTabDownload
+-SBNavigationObserverBrowserTest.NewTabDownloadWithDataURL
+-SBNavigationObserverBrowserTest.SubFrameNewTabDownload
+
 # https://crbug.com/652767: NavigationHandle::GetResponseHeaders sometimes
 # returns null for browser-side navigations
 -PageLoadMetricsBrowserTest.Ignore204Pages
diff --git a/third_party/WebKit/LayoutTests/TestExpectations b/third_party/WebKit/LayoutTests/TestExpectations
index a5e7220..f831891 100644
--- a/third_party/WebKit/LayoutTests/TestExpectations
+++ b/third_party/WebKit/LayoutTests/TestExpectations
@@ -1924,7 +1924,6 @@
 crbug.com/659917 virtual/mojo-loading/http/tests/xmlhttprequest/workers/xmlhttprequest-response-type-blob-sync.html [ Pass Timeout ]
 crbug.com/659917 virtual/mojo-loading/http/tests/xmlhttprequest/workers/shared-worker-response-type-blob-sync.html [ Pass Timeout ]
 
-crbug.com/669357 virtual/mojo-loading/http/tests/inspector/network/network-fetch.html [ Failure ]
 crbug.com/669357 virtual/mojo-loading/http/tests/inspector-protocol/network-data-length.html [ Failure ]
 crbug.com/669357 virtual/mojo-loading/http/tests/inspector/network/network-datareceived.html [ Failure ]
 crbug.com/669357 virtual/mojo-loading/http/tests/inspector/tracing/timeline-receive-response-event.html [ Failure ]
@@ -2215,4 +2214,4 @@
 
 # Added 2016-12-14
 crbug.com/674048 [ Linux ] virtual/mojo-loading/http/tests/navigation/image-load-in-unload-handler.html [ Pass Timeout ]
-crbug.com/674123 virtual/threaded/animations/animation-direction-normal.html [ Failure Timeout ]
+crbug.com/674396 [ Win ] compositing/reflections/nested-reflection-transition.html [ Pass Failure ]
diff --git a/third_party/WebKit/LayoutTests/animations/animation-direction-normal-expected.html b/third_party/WebKit/LayoutTests/animations/animation-direction-normal-expected.html
deleted file mode 100644
index 8db4550..0000000
--- a/third_party/WebKit/LayoutTests/animations/animation-direction-normal-expected.html
+++ /dev/null
@@ -1,43 +0,0 @@
-<html lang="en">
-<head>
-  <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
-  <title>Test of animation-direction</title>
-  <style type="text/css" media="screen">
-    body {
-      margin: 0;
-    }
-
-    #box {
-      position: absolute;
-      left: 0px;
-      top: 100px;
-      height: 100px;
-      width: 100px;
-      background-color: red;
-      margin: 0;
-      transform: translateX(50px);
-    }
-    #safezone {
-      position: absolute;
-      top: 100px;
-      height: 100px;
-      width: 130px;
-      left: 30px;
-      background-color: green;
-    }
-  </style>
-  </script>
-</head>
-<body>
-<!-- This tests the operation of animation-direction. After 1 second the red boxes should be hidden by the green boxes. You should see no red boxes. -->
-<div id="box"></div>
-<div id="safezone"></div>
-<div id="result">
-Warning this test is running in real-time and may be flaky.<br>
-PASS - "transform" property for "box" element at 0.5s saw something close to: 1,0,0,1,50,0<br>
-PASS - "transform" property for "box" element at 1s saw something close to: 1,0,0,1,100,0<br>
-PASS - "transform" property for "box" element at 2.5s saw something close to: 1,0,0,1,50,0
-</div>
-</div>
-</body>
-</html>
diff --git a/third_party/WebKit/LayoutTests/animations/animation-direction-normal.html b/third_party/WebKit/LayoutTests/animations/animation-direction-normal.html
index a1f31bd..feb0adba 100644
--- a/third_party/WebKit/LayoutTests/animations/animation-direction-normal.html
+++ b/third_party/WebKit/LayoutTests/animations/animation-direction-normal.html
@@ -1,67 +1,39 @@
-<html lang="en">
-<head>
-  <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
-  <title>Test of animation-direction</title>
-  <style type="text/css" media="screen">
-    body {
-      margin: 0;
-    }
-
-    #box {
-      position: absolute;
-      left: 0px;
-      top: 100px;
-      height: 100px;
-      width: 100px;
-      background-color: red;
-      margin: 0;
-      animation-duration: 2s;
+<!DOCTYPE html>
+<title>Test of animation-direction</title>
+<script src="../resources/testharness.js"></script>
+<script src="../resources/testharnessreport.js"></script>
+<style>
+    #target {
       animation-direction: normal;
+      animation-duration: 2s;
       animation-iteration-count: 2;
-      animation-timing-function: linear;
       animation-name: move1;
-    }
-    #safezone {
+      animation-play-state: paused;
+      animation-timing-function: linear;
+      background-color: red;
+      height: 100px;
+      left: 0px;
+      margin: 0;
       position: absolute;
       top: 100px;
-      height: 100px;
-      width: 130px;
-      left: 30px;
-      background-color: green;
+      width: 100px;
     }
     @keyframes move1 {
-      from { transform: translateX(0px); }
-      to   { transform: translateX(200px); }
-    }
-  </style>
-  <script src="resources/animation-test-helpers.js" type="text/javascript" charset="utf-8"></script>
-  <script type="text/javascript" charset="utf-8">
-    const expectedValues = [
-      // [time, element-id, property, expected-value, tolerance]
-      [0.5, "box", "transform", [1,0,0,1, 50,0], 20],
-      [1.0, "box", "transform", [1,0,0,1,100,0], 20],
-      [2.5, "box", "transform", [1,0,0,1, 50,0], 20],
-    ];
-
-    function pauseAnimation()
-    {
-        document.getElementById("box").style.animationPlayState = "paused";
+      from { left: 0px; }
+      to   { left: 200px; }
     }
 
-    function setTimers()
-    {
-        setTimeout(pauseAnimation, 2500);
-    }
+</style>
+<div id="target"></div>
+<script>
+  test(function() {
+    target.style.animationDelay = '-0.5s';
+    assert_equals(getComputedStyle(target).left, '50px');
 
-    runAnimationTest(expectedValues, setTimers, null, true, true);
+    target.style.animationDelay = '-1s';
+    assert_equals(getComputedStyle(target).left, '100px');
 
-  </script>
-</head>
-<body>
-<!-- This tests the operation of animation-direction. After 1 second the red boxes should be hidden by the green boxes. You should see no red boxes. -->
-<div id="box"></div>
-<div id="safezone"></div>
-<div id="result"></div>
-</div>
-</body>
-</html>
+    target.style.animationDelay = '-2.5s';
+    assert_equals(getComputedStyle(target).left, '50px');
+  }, "animation-direction normal plays forwards");
+</script>
diff --git a/third_party/WebKit/LayoutTests/animations/transition-zoomed-length.html b/third_party/WebKit/LayoutTests/animations/transition-zoomed-length.html
new file mode 100644
index 0000000..6a8e9f4f
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/animations/transition-zoomed-length.html
@@ -0,0 +1,83 @@
+<!DOCTYPE html>
+<script src="../resources/testharness.js"></script>
+<script src="../resources/testharnessreport.js"></script>
+<style>
+#target {
+  transition: 1s;
+  border-style: solid;
+  outline-style: solid;
+  column-rule-style: solid;
+}
+</style>
+<div id="target"></div>
+<script>
+var lengthProperties = [
+  'baselineShift',
+  'borderBottomWidth',
+  'borderLeftWidth',
+  'borderRightWidth',
+  'borderTopWidth',
+  'bottom',
+  'cx',
+  'cy',
+  'flexBasis',
+  'height',
+  'left',
+  'letterSpacing',
+  'marginBottom',
+  'marginLeft',
+  'marginRight',
+  'marginTop',
+  'maxHeight',
+  'maxWidth',
+  'minHeight',
+  'minWidth',
+  'offsetDistance',
+  'outlineOffset',
+  'outlineWidth',
+  'paddingBottom',
+  'paddingLeft',
+  'paddingRight',
+  'paddingTop',
+  'perspective',
+  'r',
+  'right',
+  'rx',
+  'ry',
+  'shapeMargin',
+  'strokeDashoffset',
+  'strokeWidth',
+  'top',
+  'verticalAlign',
+  'webkitBorderHorizontalSpacing',
+  'webkitBorderVerticalSpacing',
+  'columnGap',
+  'columnRuleWidth',
+  'columnWidth',
+  'webkitPerspectiveOriginX',
+  'webkitPerspectiveOriginY',
+  'webkitTransformOriginX',
+  'webkitTransformOriginY',
+  'webkitTransformOriginZ',
+  'width',
+  'wordSpacing',
+  'x',
+  'y',
+  'lineHeight',
+];
+var expected = {};
+
+setup(() => {
+  for (var property of lengthProperties) {
+    target.style[property] = '10px';
+    expected[property] = getComputedStyle(target)[property];
+  }
+  internals.setZoomFactor(2);
+});
+
+for (var property of lengthProperties) {
+  test(() => {
+    assert_equals(getComputedStyle(target)[property], expected[property]);
+  }, 'Computed value of transitionable ' + property + ' should not change when zoom changes');
+}
+</script>
diff --git a/third_party/WebKit/LayoutTests/fast/css/invalidation/independent-inheritance-fast-path.html b/third_party/WebKit/LayoutTests/fast/css/invalidation/independent-inheritance-fast-path.html
index f8ad807..f444407 100644
--- a/third_party/WebKit/LayoutTests/fast/css/invalidation/independent-inheritance-fast-path.html
+++ b/third_party/WebKit/LayoutTests/fast/css/invalidation/independent-inheritance-fast-path.html
@@ -19,6 +19,7 @@
     ["emptyCells", "show", "hide"],
     ["captionSide", "left", "right"],
     ["listStylePosition", "outside", "inside"],
+    ["webkitBoxDirection", "normal", "reverse"],
 ];
 
 independent_properties.forEach(function(test_data)
diff --git a/third_party/WebKit/LayoutTests/fast/forms/associatedFormControls-leak-nodes-expected.txt b/third_party/WebKit/LayoutTests/fast/forms/associatedFormControls-leak-nodes-expected.txt
deleted file mode 100644
index ae3ed3f8..0000000
--- a/third_party/WebKit/LayoutTests/fast/forms/associatedFormControls-leak-nodes-expected.txt
+++ /dev/null
@@ -1,5 +0,0 @@
-PASS documentsBefore - 1 is documentsAfter
-PASS successfullyParsed is true
-
-TEST COMPLETE
-
diff --git a/third_party/WebKit/LayoutTests/fast/forms/associatedFormControls-leak-nodes.html b/third_party/WebKit/LayoutTests/fast/forms/associatedFormControls-leak-nodes.html
deleted file mode 100644
index d1eed450..0000000
--- a/third_party/WebKit/LayoutTests/fast/forms/associatedFormControls-leak-nodes.html
+++ /dev/null
@@ -1,40 +0,0 @@
-<!DOCTYPE html>
-<html>
-<body>
-<iframe id="frame"></iframe>
-
-<script src="../../resources/js-test.js"></script>
-<script>
-if (!window.internals) {
-    debug("This test only runs on \"content_shell --run-layout-test\", as it requires existence of window.internals.");
-} else {
-    testRunner.waitUntilDone();
-    window.jsTestIsAsync = true;
-    var documentsBefore;
-    var documentsAfter;
-    // FIXME(keishi): Calling asyncGC twice to fix flakiness, crbug.com/674194
-    asyncGC(function() {
-        asyncGC(function() {
-            documentsBefore = window.internals.numberOfLiveDocuments();
-
-            var frame = document.getElementById('frame');
-            frame.contentDocument.body.innerHTML = '<form></form>';
-            document.body.removeChild(frame);
-            frame = null;
-
-            // FIXME(keishi): crbug.com/674194
-            asyncGC(function() {
-                asyncGC(function() {
-                    documentsAfter = window.internals.numberOfLiveDocuments();
-
-                    // -1 is from removing frame itself.
-                    shouldBe('documentsBefore - 1', 'documentsAfter');
-                    finishJSTest();
-                });
-            });
-        });
-    });
-}
-</script>
-</body>
-</html>
diff --git a/third_party/WebKit/LayoutTests/http/tests/cache/location-reload.html b/third_party/WebKit/LayoutTests/http/tests/cache/location-reload.html
new file mode 100644
index 0000000..ded56b6
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/http/tests/cache/location-reload.html
@@ -0,0 +1,40 @@
+<!DOCYUPE html>
+<script src="../resources/testharness.js"></script>
+<script src="../resources/testharnessreport.js"></script>
+<script>
+
+async_test(t => {
+  let testWindow = null;
+  let messageCount = 0;
+
+  // Test will run on testWindow that is opened below.
+  // This page just received messages to confirm if tests run expectedly.
+  const checkReady = e => {
+    t.step(() => {
+      // Will receive "READY" twice because of a reload, then receive "PASS".
+      assert_equals(e.data, "READY", "received message is " + e.data);
+      messageCount++;
+
+      if (messageCount == 2) {
+        window.removeEventListener("message", checkReady, false);
+        window.addEventListener("message", e => {
+          assert_equals(e.data, "PASS", "received message is " + e.data);
+          t.done();
+        }, { once: true });
+      }
+
+      // Send back "START" message for "READY".
+      assert_class_string(testWindow, "Window", "testWindow is invalid");
+      testWindow.postMessage("START", location.origin);
+    });
+  };
+  window.addEventListener("message", checkReady, false);
+
+  // Start a test in a dedicated window because we can not track navigations
+  // within a test harness.
+  t.step(() => {
+    testWindow = open("./resources/location-reload-window.html", "testWindow");
+    assert_class_string(testWindow, "Window", "window.open() failed");
+  });
+}, "Test location.reload() cache behaviors");
+</script>
diff --git a/third_party/WebKit/LayoutTests/http/tests/cache/resources/location-reload-window.html b/third_party/WebKit/LayoutTests/http/tests/cache/resources/location-reload-window.html
new file mode 100644
index 0000000..71b5970
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/http/tests/cache/resources/location-reload-window.html
@@ -0,0 +1,48 @@
+<!DOCTYPE html>
+<html>
+<body>
+<h1>random</h1>
+<script src="./random-cached.cgi"></script>
+<script>
+const lastRandomNumberKey = "lastRandomNumber";
+
+window.addEventListener("message", e => {
+  if (e.data != "START") {
+    window.opener.postMessage("FAIL: unknown; " + e.data, location.origin);
+  } else {
+    // window.top.randomNumber should be set by random-cached.cgi.
+    if (window.top === undefined || window.top.randomNumber === undefined) {
+      window.opener.postMessage("FAIL: randomNumber isn't defined",
+                                location.origin);
+      return;
+    }
+
+    // If location.reload() is already triggered, lastRandomNumberKey should be
+    // stored in the sessionStorage. Otherwise, this is the first load.
+    const lastRandomNumberString = sessionStorage.getItem(lastRandomNumberKey);
+    if (lastRandomNumberString !== null) {
+      // sessionStorage returns DOMString and need to be converted to Number.
+      const lastRandomNumber = Number(lastRandomNumberString);
+
+      // Because the random-cached.cgi is a sub-resource, and set HTTP headers
+      // to allow caching, location.reload() should follow the cache-protocol to
+      // reuse the cached resource. That is to say the randomNumber should not
+      // be changed on the reload.
+      window.opener.postMessage(lastRandomNumber == top.randomNumber
+                                    ? "PASS"
+                                    : "FAIL: randomNumber was changed",
+                                location.origin);
+    } else {
+      // Store the first randomNumber to the sessionStorage, and call reload().
+      // This window will send "READY" again, then receive "START".
+      sessionStorage.setItem(lastRandomNumberKey, top.randomNumber);
+      location.reload();
+    }
+  }
+}, false);
+
+// Send "READY" message first so that parent window can ensure to send messages.
+window.opener.postMessage("READY", location.origin);
+</script>
+</body>
+</html>
diff --git a/third_party/WebKit/LayoutTests/http/tests/inspector/network-test.js b/third_party/WebKit/LayoutTests/http/tests/inspector/network-test.js
index 470d3d7..92ccdd6 100644
--- a/third_party/WebKit/LayoutTests/http/tests/inspector/network-test.js
+++ b/third_party/WebKit/LayoutTests/http/tests/inspector/network-test.js
@@ -45,7 +45,11 @@
 
 function makeFetch(url, requestInitializer)
 {
-    return fetch(url, requestInitializer).catch(e => e);
+    return fetch(url, requestInitializer).then(res => {
+        // Call text(). Otherwise the backpressure mechanism may block loading.
+        res.text();
+        return res;
+    }).catch(e => e);
 }
 
 var initialize_NetworkTest = function() {
diff --git a/third_party/WebKit/LayoutTests/http/tests/inspector/persistence/persistence-sync-content-expected.txt b/third_party/WebKit/LayoutTests/http/tests/inspector/persistence/persistence-sync-content-expected.txt
index 1e5de22..65a221a 100644
--- a/third_party/WebKit/LayoutTests/http/tests/inspector/persistence/persistence-sync-content-expected.txt
+++ b/third_party/WebKit/LayoutTests/http/tests/inspector/persistence/persistence-sync-content-expected.txt
@@ -18,6 +18,10 @@
 network code: 'window.foo4 = 4;'
 fileSystem code: 'window.foo4 = 4;'
 
+Running: resetFileSystemWorkingCopy
+network code: 'window.foo3 = 3;'
+fileSystem code: 'window.foo3 = 3;'
+
 Running: setNetworkRevision
 network code: 'window.foo2 = 2;'
 fileSystem code: 'window.foo2 = 2;'
diff --git a/third_party/WebKit/LayoutTests/http/tests/inspector/persistence/persistence-sync-content.html b/third_party/WebKit/LayoutTests/http/tests/inspector/persistence/persistence-sync-content.html
index fd57e59e..c4aef5d 100644
--- a/third_party/WebKit/LayoutTests/http/tests/inspector/persistence/persistence-sync-content.html
+++ b/third_party/WebKit/LayoutTests/http/tests/inspector/persistence/persistence-sync-content.html
@@ -46,6 +46,12 @@
             fileSystemCode.setWorkingCopy("window.foo4 = 4;");
         },
 
+        function resetFileSystemWorkingCopy(next)
+        {
+            InspectorTest.addSniffer(Persistence.Persistence.prototype, "_contentSyncedForTest", dumpWorkingCopiesAndNext.bind(null, next));
+            fileSystemCode.resetWorkingCopy();
+        },
+
         function setNetworkRevision(next)
         {
             InspectorTest.addSniffer(Persistence.Persistence.prototype, "_contentSyncedForTest", dumpWorkingCopiesAndNext.bind(null, next));
diff --git a/third_party/WebKit/LayoutTests/http/tests/inspector/timeline-test.js b/third_party/WebKit/LayoutTests/http/tests/inspector/timeline-test.js
index 0adef3b..c1ad4c83 100644
--- a/third_party/WebKit/LayoutTests/http/tests/inspector/timeline-test.js
+++ b/third_party/WebKit/LayoutTests/http/tests/inspector/timeline-test.js
@@ -410,6 +410,40 @@
     InspectorTest.addArray(TimelineModel.InvalidationTracker.invalidationEventsFor(record._event), InspectorTest.InvalidationFormatters, "", comment);
 }
 
+InspectorTest.dumpFlameChartProvider = function(provider, includeGroups)
+{
+    var includeGroupsSet = includeGroups && new Set(includeGroups);
+    var timelineData = provider.timelineData();
+    var stackDepth = provider.maxStackDepth();
+    var entriesByLevel = new Multimap();
+
+    for (let i = 0; i < timelineData.entryLevels.length; ++i)
+        entriesByLevel.set(timelineData.entryLevels[i], i);
+
+    for (let groupIndex = 0; groupIndex < timelineData.groups.length; ++groupIndex) {
+        const group = timelineData.groups[groupIndex];
+        if (includeGroupsSet && !includeGroupsSet.has(group.name))
+            continue;
+        var maxLevel = groupIndex + 1 < timelineData.groups.length ? timelineData.groups[groupIndex + 1].firstLevel : stackDepth;
+        InspectorTest.addResult(`Group: ${group.name}`);
+        for (let level = group.startLevel; level < maxLevel; ++level) {
+            InspectorTest.addResult(`Level ${level - group.startLevel}`);
+            var entries = entriesByLevel.get(level);
+            for (const index of entries) {
+                const title = provider.entryTitle(index);
+                const color = provider.entryColor(index);
+                InspectorTest.addResult(`${title} (${color})`);
+            }
+        }
+    }
+}
+
+InspectorTest.dumpTimelineFlameChart = function(includeGroups) {
+    const provider = UI.panels.timeline._flameChart._dataProvider;
+    InspectorTest.addResult('Timeline Flame Chart');
+    InspectorTest.dumpFlameChartProvider(provider, includeGroups);
+}
+
 InspectorTest.FakeFileReader.prototype = {
     start: function(output)
     {
diff --git a/third_party/WebKit/LayoutTests/http/tests/inspector/tracing/timeline-network-received-data.html b/third_party/WebKit/LayoutTests/http/tests/inspector/tracing/timeline-network-received-data.html
index 298ea98d..eb3a42e 100644
--- a/third_party/WebKit/LayoutTests/http/tests/inspector/tracing/timeline-network-received-data.html
+++ b/third_party/WebKit/LayoutTests/http/tests/inspector/tracing/timeline-network-received-data.html
@@ -8,11 +8,13 @@
 {
     var image = new Image();
     var imagePromise = new Promise((fulfill) => image.onload = fulfill);
-    image.src = "resources/anImage.png";
+    // Use random urls to avoid caching.
+    const random = Math.random();
+    image.src = "resources/anImage.png?random=" + random;
 
     var scriptPromise = new Promise((fulfill) => window.timelineNetworkResourceEvaluated = fulfill);
     var script = document.createElement("script");
-    script.src = "resources/timeline-network-resource.js";
+    script.src = "resources/timeline-network-resource.js?randome=" + random;
     document.body.appendChild(script);
 
     return Promise.all([imagePromise, scriptPromise]);
diff --git a/third_party/WebKit/LayoutTests/http/tests/inspector/tracing/timeline-receive-response-event.html b/third_party/WebKit/LayoutTests/http/tests/inspector/tracing/timeline-receive-response-event.html
index c20c40a..7ec1251 100644
--- a/third_party/WebKit/LayoutTests/http/tests/inspector/tracing/timeline-receive-response-event.html
+++ b/third_party/WebKit/LayoutTests/http/tests/inspector/tracing/timeline-receive-response-event.html
@@ -10,13 +10,15 @@
     var promise = new Promise((fulfill) => callback = fulfill);
     var image = new Image();
     image.onload = bar;
-    image.src = "resources/anImage.png";
+    // Use random urls to avoid caching.
+    const random = Math.random();
+    image.src = "resources/anImage.png?random=" + random;
 
     function bar()
     {
         var image = new Image();
         image.onload = function(event) { callback(); }  // do not pass event argument to the callback.
-        image.src = "resources/anotherImage.png";
+        image.src = "resources/anotherImage.png?random=" + random;
     }
     return promise;
 }
diff --git a/third_party/WebKit/LayoutTests/inspector/elements/styles-1/css-outline.html b/third_party/WebKit/LayoutTests/inspector/elements/styles-1/css-outline.html
index 1590dd8..91db7b90 100644
--- a/third_party/WebKit/LayoutTests/inspector/elements/styles-1/css-outline.html
+++ b/third_party/WebKit/LayoutTests/inspector/elements/styles-1/css-outline.html
@@ -60,17 +60,17 @@
 
 function test()
 {
-    function onStyleSheetParsed(rules)
+    function onRulesParsed(isLastChunk, rules)
     {
         for (var i = 0; i < rules.length; ++i)
             InspectorTest.addObject(rules[i]);
-        InspectorTest.completeTest();
+        if (isLastChunk)
+            InspectorTest.completeTest();
     }
 
     function onStyleFetched(result)
     {
-        var parser = new SDK.CSSParser();
-        parser.parse(result.value, onStyleSheetParsed);
+        Common.formatterWorkerPool.parseCSS(result.value, onRulesParsed);
     }
 
     InspectorTest.evaluateInPage("getCSS()", onStyleFetched);
diff --git a/third_party/WebKit/LayoutTests/inspector/extensions/extensions-timeline-api-expected.txt b/third_party/WebKit/LayoutTests/inspector/extensions/extensions-timeline-api-expected.txt
index 7bb476b..d920e90 100644
--- a/third_party/WebKit/LayoutTests/inspector/extensions/extensions-timeline-api-expected.txt
+++ b/third_party/WebKit/LayoutTests/inspector/extensions/extensions-timeline-api-expected.txt
@@ -3,6 +3,7 @@
  Started extension.
 Running tests...
 RUNNING TEST: extension_testTimeline
+TraceProvider:
 {
     onRecordingStarted : {
         addListener : <function>
@@ -13,7 +14,21 @@
         removeListener : <function>
     }
 }
+Provider short display name: extension trace provider
+Provider long display name: long extension name
 traceProvider.onRecordingStarted fired.
+TracingSession:
+{
+    complete : <function>
+}
 traceProvider.onRecordingStopped fired.
+Timeline Flame Chart
+Group: long extension name
+Level 0
+Extension record X 1 (hsla(261, 82%, 70%, 0.7))
+Extension record X 2 (hsla(230, 88%, 70%, 0.7))
+Level 1
+Extension record I 1 (hsla(298, 100%, 70%, 0.7))
+Extension record B+E (hsla(292, 100%, 70%, 0.7))
 All tests done.
 
diff --git a/third_party/WebKit/LayoutTests/inspector/extensions/extensions-timeline-api.html b/third_party/WebKit/LayoutTests/inspector/extensions/extensions-timeline-api.html
index 1ed18900..c216f2a 100644
--- a/third_party/WebKit/LayoutTests/inspector/extensions/extensions-timeline-api.html
+++ b/third_party/WebKit/LayoutTests/inspector/extensions/extensions-timeline-api.html
@@ -5,33 +5,82 @@
 <script src="../../http/tests/inspector/timeline-test.js"></script>
 
 <script type="text/javascript">
+function initialize_timelineExtensionTest()
+{
+
+InspectorTest.enableTimelineExtensionAndStart = function(callback) {
+    const provider = Extensions.extensionServer.traceProviders().peekLast();
+    const timelinePanel = UI.panels.timeline;
+    const setting = Timeline.TimelinePanel._settingForTraceProvider(provider);
+    setting.set(true);
+    InspectorTest.addResult(`Provider short display name: ${provider.shortDisplayName()}`);
+    InspectorTest.addResult(`Provider long display name: ${provider.longDisplayName()}`);
+    InspectorTest.startTimeline(callback);
+}
+
+}
+
 function extension_testTimeline(nextTest)
 {
-    function onRecordingStarted()
+    var session;
+    var sessionTimeOffset;
+    var startTime;
+
+    function onRecordingStarted(s)
     {
+        sessionTimeOffset = (Date.now() - performance.now()) * 1000;
+        startTime = performance.now();
         output("traceProvider.onRecordingStarted fired.");
-    }
-    function onRecordingStopped()
-    {
+        output("TracingSession:");
+        dumpObject(s);
+        session = s;
+     }
+
+     function onRecordingStopped()
+     {
         output("traceProvider.onRecordingStopped fired.");
-        nextTest();
+
+        const endTime = performance.now();
+        var pid = 1;
+        var tid = 1;
+        var step = (endTime - startTime) * 1000 / 10;
+        var start = startTime * 1000;
+        var data = { "traceEvents": [
+            {"name": "Extension record X 1", "ts": start, "dur": step * 4, "ph": "X", "args": {},  "tid": tid, "pid": pid, "cat":"" },
+            {"name": "Extension record X 2", "ts": start + step * 5, "dur": step * 5, "ph": "X", "args": {},  "tid": tid, "pid": pid, "cat":"" },
+            {"name": "Extension record I 1", "ts": start + step * 5.5, "ph": "I", "args": {},  "tid": tid, "pid": pid, "cat":"" },
+            {"name": "Extension record B+E", "ts": start + step * 6, "ph": "B", "args": {}, "tid": tid, "pid": pid, "cat":"" },
+            {"name": "Extension record B+E", "ts": start + step * 10, "ph": "E", "args": {}, "tid": tid, "pid": pid, "cat":"" }
+        ]};
+        var url = "data:application/json," + escape(JSON.stringify(data));
+        session.complete(url, sessionTimeOffset);
     }
-    var traceProvider = webInspector.timeline.addTraceProvider("extension trace provider", "tooltip");
+
+    var traceProvider = webInspector.timeline.addTraceProvider("extension trace provider", "long extension name");
+    output("TraceProvider:");
     dumpObject(traceProvider);
     traceProvider.onRecordingStarted.addListener(onRecordingStarted);
     traceProvider.onRecordingStopped.addListener(onRecordingStopped);
-    extension_startTimeline(() => extension_stopTimeline(() => {}));
+    extension_startTimeline(
+        () => extension_stopTimeline(
+            () => extension_dumpFlameChart(nextTest)));
 }
 
 function extension_startTimeline(callback)
 {
-    evaluateOnFrontend("InspectorTest.startTimeline(reply);", callback);
+    evaluateOnFrontend("InspectorTest.enableTimelineExtensionAndStart(reply);", callback);
 }
 
 function extension_stopTimeline(callback)
 {
     evaluateOnFrontend("InspectorTest.stopTimeline(reply);", callback);
 }
+
+function extension_dumpFlameChart(callback)
+{
+    evaluateOnFrontend("InspectorTest.dumpTimelineFlameChart(['long extension name']); reply()", callback);
+}
+
 </script>
 </head>
 <body onload="runTest()">
diff --git a/third_party/WebKit/LayoutTests/inspector/tracing/buffer-usage.html b/third_party/WebKit/LayoutTests/inspector/tracing/buffer-usage.html
index 0e2ec1a..a7309171 100644
--- a/third_party/WebKit/LayoutTests/inspector/tracing/buffer-usage.html
+++ b/third_party/WebKit/LayoutTests/inspector/tracing/buffer-usage.html
@@ -43,13 +43,11 @@
         {
             InspectorTest.addResult("TimelineLifecycleDelegate.loadingComplete");
             InspectorTest.completeTest();
-        },
-
-        __proto__: Timeline.TimelineLifecycleDelegate
+        }
     };
 
     var controller = new Timeline.TimelineController(SDK.targetManager.mainTarget(), new TestTimelineLifecycleDelegate(), InspectorTest.createTracingModel());
-    controller.startRecording();
+    controller.startRecording({}, []);
 }
 
 </script>
diff --git a/third_party/WebKit/Source/build/scripts/make_computed_style_base.py b/third_party/WebKit/Source/build/scripts/make_computed_style_base.py
index b88d4132..6bda8f46 100755
--- a/third_party/WebKit/Source/build/scripts/make_computed_style_base.py
+++ b/third_party/WebKit/Source/build/scripts/make_computed_style_base.py
@@ -88,7 +88,7 @@
         self._fields = []
         for property in self._properties.values():
             if property['keyword_only']:
-                property_name = property['upper_camel_name']
+                property_name = property['name_for_methods']
                 if property['name_for_methods']:
                     property_name = property['name_for_methods']
                 property_name_lower = property_name[0].lower() + property_name[1:]
@@ -106,7 +106,7 @@
                 # If the property is independent, add the single-bit sized isInherited flag
                 # to the list of Fields as well.
                 if property['independent']:
-                    field_name_suffix_upper = property['upper_camel_name'] + 'IsInherited'
+                    field_name_suffix_upper = property['name_for_methods'] + 'IsInherited'
                     field_name_suffix_lower = property_name_lower + 'IsInherited'
                     self._fields.append(Field(
                         'inherited_flag',
diff --git a/third_party/WebKit/Source/core/BUILD.gn b/third_party/WebKit/Source/core/BUILD.gn
index e32d036..cbd2e0fb 100644
--- a/third_party/WebKit/Source/core/BUILD.gn
+++ b/third_party/WebKit/Source/core/BUILD.gn
@@ -1145,6 +1145,7 @@
     "html/HTMLImageElementTest.cpp",
     "html/HTMLInputElementTest.cpp",
     "html/HTMLLinkElementSizesAttributeTest.cpp",
+    "html/HTMLLinkElementTest.cpp",
     "html/HTMLMediaElementTest.cpp",
     "html/HTMLOutputElementTest.cpp",
     "html/HTMLSelectElementTest.cpp",
diff --git a/third_party/WebKit/Source/core/animation/BUILD.gn b/third_party/WebKit/Source/core/animation/BUILD.gn
index b72f1034..029004a 100644
--- a/third_party/WebKit/Source/core/animation/BUILD.gn
+++ b/third_party/WebKit/Source/core/animation/BUILD.gn
@@ -56,8 +56,8 @@
     "CSSLengthPairInterpolationType.h",
     "CSSNumberInterpolationType.cpp",
     "CSSNumberInterpolationType.h",
-    "CSSOffsetRotationInterpolationType.cpp",
-    "CSSOffsetRotationInterpolationType.h",
+    "CSSOffsetRotateInterpolationType.cpp",
+    "CSSOffsetRotateInterpolationType.h",
     "CSSPaintInterpolationType.cpp",
     "CSSPaintInterpolationType.h",
     "CSSPathInterpolationType.cpp",
diff --git a/third_party/WebKit/Source/core/animation/CSSInterpolationTypesMap.cpp b/third_party/WebKit/Source/core/animation/CSSInterpolationTypesMap.cpp
index f67ad6f..7622bb5 100644
--- a/third_party/WebKit/Source/core/animation/CSSInterpolationTypesMap.cpp
+++ b/third_party/WebKit/Source/core/animation/CSSInterpolationTypesMap.cpp
@@ -18,7 +18,7 @@
 #include "core/animation/CSSLengthListInterpolationType.h"
 #include "core/animation/CSSLengthPairInterpolationType.h"
 #include "core/animation/CSSNumberInterpolationType.h"
-#include "core/animation/CSSOffsetRotationInterpolationType.h"
+#include "core/animation/CSSOffsetRotateInterpolationType.h"
 #include "core/animation/CSSPaintInterpolationType.h"
 #include "core/animation/CSSPathInterpolationType.h"
 #include "core/animation/CSSPositionAxisListInterpolationType.h"
@@ -197,7 +197,7 @@
     case CSSPropertyOffsetRotation:
     case CSSPropertyOffsetRotate:
       applicableTypes->push_back(
-          WTF::makeUnique<CSSOffsetRotationInterpolationType>(usedProperty));
+          WTF::makeUnique<CSSOffsetRotateInterpolationType>(usedProperty));
       break;
     case CSSPropertyBackgroundPositionX:
     case CSSPropertyBackgroundPositionY:
diff --git a/third_party/WebKit/Source/core/animation/CSSOffsetRotationInterpolationType.cpp b/third_party/WebKit/Source/core/animation/CSSOffsetRotateInterpolationType.cpp
similarity index 89%
rename from third_party/WebKit/Source/core/animation/CSSOffsetRotationInterpolationType.cpp
rename to third_party/WebKit/Source/core/animation/CSSOffsetRotateInterpolationType.cpp
index 3c76b9db..f1d1d08 100644
--- a/third_party/WebKit/Source/core/animation/CSSOffsetRotationInterpolationType.cpp
+++ b/third_party/WebKit/Source/core/animation/CSSOffsetRotateInterpolationType.cpp
@@ -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 "core/animation/CSSOffsetRotationInterpolationType.h"
+#include "core/animation/CSSOffsetRotateInterpolationType.h"
 
 #include "core/css/resolver/StyleBuilderConverter.h"
 #include "core/style/StyleOffsetRotation.h"
@@ -90,7 +90,7 @@
 
 }  // namespace
 
-InterpolationValue CSSOffsetRotationInterpolationType::maybeConvertNeutral(
+InterpolationValue CSSOffsetRotateInterpolationType::maybeConvertNeutral(
     const InterpolationValue& underlying,
     ConversionCheckers& conversionCheckers) const {
   OffsetRotationType underlyingRotationType =
@@ -101,13 +101,13 @@
   return convertOffsetRotate(StyleOffsetRotation(0, underlyingRotationType));
 }
 
-InterpolationValue CSSOffsetRotationInterpolationType::maybeConvertInitial(
+InterpolationValue CSSOffsetRotateInterpolationType::maybeConvertInitial(
     const StyleResolverState&,
     ConversionCheckers& conversionCheckers) const {
   return convertOffsetRotate(StyleOffsetRotation(0, OffsetRotationAuto));
 }
 
-InterpolationValue CSSOffsetRotationInterpolationType::maybeConvertInherit(
+InterpolationValue CSSOffsetRotateInterpolationType::maybeConvertInherit(
     const StyleResolverState& state,
     ConversionCheckers& conversionCheckers) const {
   OffsetRotationType inheritedRotationType =
@@ -117,15 +117,14 @@
   return convertOffsetRotate(state.parentStyle()->offsetRotation());
 }
 
-InterpolationValue CSSOffsetRotationInterpolationType::maybeConvertValue(
+InterpolationValue CSSOffsetRotateInterpolationType::maybeConvertValue(
     const CSSValue& value,
     const StyleResolverState&,
     ConversionCheckers&) const {
   return convertOffsetRotate(StyleBuilderConverter::convertOffsetRotate(value));
 }
 
-PairwiseInterpolationValue
-CSSOffsetRotationInterpolationType::maybeMergeSingles(
+PairwiseInterpolationValue CSSOffsetRotateInterpolationType::maybeMergeSingles(
     InterpolationValue&& start,
     InterpolationValue&& end) const {
   const OffsetRotationType& startType =
@@ -142,12 +141,12 @@
 }
 
 InterpolationValue
-CSSOffsetRotationInterpolationType::maybeConvertUnderlyingValue(
+CSSOffsetRotateInterpolationType::maybeConvertUnderlyingValue(
     const InterpolationEnvironment& environment) const {
   return convertOffsetRotate(environment.state().style()->offsetRotation());
 }
 
-void CSSOffsetRotationInterpolationType::composite(
+void CSSOffsetRotateInterpolationType::composite(
     UnderlyingValueOwner& underlyingValueOwner,
     double underlyingFraction,
     const InterpolationValue& value,
@@ -159,14 +158,15 @@
   const OffsetRotationType& rotationType =
       toCSSOffsetRotationNonInterpolableValue(*value.nonInterpolableValue)
           .rotationType();
-  if (underlyingType == rotationType)
+  if (underlyingType == rotationType) {
     underlyingValueOwner.mutableValue().interpolableValue->scaleAndAdd(
         underlyingFraction, *value.interpolableValue);
-  else
+  } else {
     underlyingValueOwner.set(*this, value);
+  }
 }
 
-void CSSOffsetRotationInterpolationType::apply(
+void CSSOffsetRotateInterpolationType::apply(
     const InterpolableValue& interpolableValue,
     const NonInterpolableValue* nonInterpolableValue,
     InterpolationEnvironment& environment) const {
diff --git a/third_party/WebKit/Source/core/animation/CSSOffsetRotationInterpolationType.h b/third_party/WebKit/Source/core/animation/CSSOffsetRotateInterpolationType.h
similarity index 85%
rename from third_party/WebKit/Source/core/animation/CSSOffsetRotationInterpolationType.h
rename to third_party/WebKit/Source/core/animation/CSSOffsetRotateInterpolationType.h
index d83662c1..060b4bd 100644
--- a/third_party/WebKit/Source/core/animation/CSSOffsetRotationInterpolationType.h
+++ b/third_party/WebKit/Source/core/animation/CSSOffsetRotateInterpolationType.h
@@ -2,16 +2,16 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CSSOffsetRotationInterpolationType_h
-#define CSSOffsetRotationInterpolationType_h
+#ifndef CSSOffsetRotateInterpolationType_h
+#define CSSOffsetRotateInterpolationType_h
 
 #include "core/animation/CSSInterpolationType.h"
 
 namespace blink {
 
-class CSSOffsetRotationInterpolationType : public CSSInterpolationType {
+class CSSOffsetRotateInterpolationType : public CSSInterpolationType {
  public:
-  CSSOffsetRotationInterpolationType(PropertyHandle property)
+  CSSOffsetRotateInterpolationType(PropertyHandle property)
       : CSSInterpolationType(property) {
     DCHECK(cssProperty() == CSSPropertyOffsetRotate ||
            cssProperty() == CSSPropertyOffsetRotation);
@@ -44,4 +44,4 @@
 
 }  // namespace blink
 
-#endif  // CSSOffsetRotationInterpolationType_h
+#endif  // CSSOffsetRotateInterpolationType_h
diff --git a/third_party/WebKit/Source/core/animation/css/CSSAnimatableValueFactory.cpp b/third_party/WebKit/Source/core/animation/css/CSSAnimatableValueFactory.cpp
index 8f455bf..57e060c 100644
--- a/third_party/WebKit/Source/core/animation/css/CSSAnimatableValueFactory.cpp
+++ b/third_party/WebKit/Source/core/animation/css/CSSAnimatableValueFactory.cpp
@@ -31,6 +31,7 @@
 #include "core/animation/css/CSSAnimatableValueFactory.h"
 
 #include "core/CSSValueKeywords.h"
+#include "core/animation/LengthPropertyFunctions.h"
 #include "core/animation/animatable/AnimatableClipPathOperation.h"
 #include "core/animation/animatable/AnimatableColor.h"
 #include "core/animation/animatable/AnimatableDouble.h"
@@ -96,6 +97,15 @@
   return createFromLengthWithZoom(length, style.effectiveZoom());
 }
 
+static PassRefPtr<AnimatableValue> createFromPropertyLength(
+    CSSPropertyID property,
+    const ComputedStyle& style) {
+  Length length;
+  bool success = LengthPropertyFunctions::getLength(property, style, length);
+  DCHECK(success);
+  return createFromLength(length, style);
+}
+
 static PassRefPtr<AnimatableValue> createFromUnzoomedLength(
     const UnzoomedLength& unzoomedLength) {
   return createFromLengthWithZoom(unzoomedLength.length(), 1);
@@ -175,7 +185,7 @@
   return AnimatableLengthPoint3D::create(
       createFromLength(transformOrigin.x(), style),
       createFromLength(transformOrigin.y(), style),
-      createFromDouble(transformOrigin.z()));
+      createFromLength(Length(transformOrigin.z(), Fixed), style));
 }
 
 inline static PassRefPtr<AnimatableValue> createFromLengthSize(
@@ -371,7 +381,7 @@
     case CSSPropertyBorderBottomRightRadius:
       return createFromLengthSize(style.borderBottomRightRadius(), style);
     case CSSPropertyBorderBottomWidth:
-      return createFromDouble(style.borderBottomWidth());
+      return createFromPropertyLength(property, style);
     case CSSPropertyBorderImageOutset:
       return createFromBorderImageLengthBox(style.borderImageOutset(), style);
     case CSSPropertyBorderImageSlice:
@@ -384,11 +394,11 @@
     case CSSPropertyBorderLeftColor:
       return createFromColor(property, style);
     case CSSPropertyBorderLeftWidth:
-      return createFromDouble(style.borderLeftWidth());
+      return createFromPropertyLength(property, style);
     case CSSPropertyBorderRightColor:
       return createFromColor(property, style);
     case CSSPropertyBorderRightWidth:
-      return createFromDouble(style.borderRightWidth());
+      return createFromPropertyLength(property, style);
     case CSSPropertyBorderTopColor:
       return createFromColor(property, style);
     case CSSPropertyBorderTopLeftRadius:
@@ -396,7 +406,7 @@
     case CSSPropertyBorderTopRightRadius:
       return createFromLengthSize(style.borderTopRightRadius(), style);
     case CSSPropertyBorderTopWidth:
-      return createFromDouble(style.borderTopWidth());
+      return createFromPropertyLength(property, style);
     case CSSPropertyBottom:
       return createFromLength(style.bottom(), style);
     case CSSPropertyBoxShadow:
@@ -454,7 +464,7 @@
     case CSSPropertyLeft:
       return createFromLength(style.left(), style);
     case CSSPropertyLetterSpacing:
-      return createFromDouble(style.letterSpacing());
+      return createFromPropertyLength(property, style);
     case CSSPropertyLineHeight:
       return createFromLineHeight(style.specifiedLineHeight(), style);
     case CSSPropertyMarginBottom:
@@ -482,9 +492,9 @@
     case CSSPropertyOutlineColor:
       return createFromColor(property, style);
     case CSSPropertyOutlineOffset:
-      return createFromDouble(style.outlineOffset());
+      return createFromPropertyLength(property, style);
     case CSSPropertyOutlineWidth:
-      return createFromDouble(style.outlineWidth());
+      return createFromPropertyLength(property, style);
     case CSSPropertyPaddingBottom:
       return createFromLength(style.paddingBottom(), style);
     case CSSPropertyPaddingLeft:
@@ -527,9 +537,9 @@
     case CSSPropertyTop:
       return createFromLength(style.top(), style);
     case CSSPropertyWebkitBorderHorizontalSpacing:
-      return createFromDouble(style.horizontalBorderSpacing());
+      return createFromPropertyLength(property, style);
     case CSSPropertyWebkitBorderVerticalSpacing:
-      return createFromDouble(style.verticalBorderSpacing());
+      return createFromPropertyLength(property, style);
     case CSSPropertyClipPath:
       if (ClipPathOperation* operation = style.clipPath())
         return AnimatableClipPathOperation::create(operation);
@@ -539,15 +549,15 @@
         return AnimatableUnknown::create(CSSValueAuto);
       return createFromDouble(style.columnCount());
     case CSSPropertyColumnGap:
-      return createFromDouble(style.columnGap());
+      return createFromPropertyLength(property, style);
     case CSSPropertyColumnRuleColor:
       return createFromColor(property, style);
     case CSSPropertyColumnRuleWidth:
-      return createFromDouble(style.columnRuleWidth());
+      return createFromPropertyLength(property, style);
     case CSSPropertyColumnWidth:
       if (style.hasAutoColumnWidth())
         return AnimatableUnknown::create(CSSValueAuto);
-      return createFromDouble(style.columnWidth());
+      return createFromPropertyLength(property, style);
     case CSSPropertyFilter:
       return AnimatableFilterOperations::create(style.filter());
     case CSSPropertyBackdropFilter:
@@ -578,7 +588,7 @@
         return AnimatableUnknown::create(
             CSSIdentifierValue::create(CSSValueNone));
       }
-      return createFromDouble(style.perspective());
+      return createFromPropertyLength(property, style);
     case CSSPropertyPerspectiveOrigin:
       return createFromLengthPoint(style.perspectiveOrigin(), style);
     case CSSPropertyShapeOutside:
@@ -636,13 +646,13 @@
     case CSSPropertyWebkitTransformOriginY:
       return createFromLength(style.transformOriginY(), style);
     case CSSPropertyWebkitTransformOriginZ:
-      return createFromDouble(style.transformOriginZ());
+      return createFromPropertyLength(property, style);
     case CSSPropertyWidows:
       return createFromDouble(style.widows());
     case CSSPropertyWidth:
       return createFromLength(style.width(), style);
     case CSSPropertyWordSpacing:
-      return createFromDouble(style.wordSpacing());
+      return createFromPropertyLength(property, style);
     case CSSPropertyVerticalAlign:
       if (style.verticalAlign() == VerticalAlignLength)
         return createFromLength(style.getVerticalAlignLength(), style);
diff --git a/third_party/WebKit/Source/core/css/CSSProperties.in b/third_party/WebKit/Source/core/css/CSSProperties.in
index 195e410d..42bb4f7 100644
--- a/third_party/WebKit/Source/core/css/CSSProperties.in
+++ b/third_party/WebKit/Source/core/css/CSSProperties.in
@@ -397,7 +397,7 @@
 -webkit-border-vertical-spacing interpolable, inherited, name_for_methods=VerticalBorderSpacing, converter=convertComputedLength<short>
 -webkit-box-align type_name=EBoxAlignment
 -webkit-box-decoration-break
--webkit-box-direction inherited, keyword_only, keywords=[normal|reverse], initial_keyword=normal
+-webkit-box-direction inherited, independent, keyword_only, keywords=[normal|reverse], initial_keyword=normal
 -webkit-box-flex type_name=float
 -webkit-box-flex-group type_name=unsigned int
 -webkit-box-lines
diff --git a/third_party/WebKit/Source/core/css/resolver/AnimatedStyleBuilder.cpp b/third_party/WebKit/Source/core/css/resolver/AnimatedStyleBuilder.cpp
index c2b859e..d64c35f 100644
--- a/third_party/WebKit/Source/core/css/resolver/AnimatedStyleBuilder.cpp
+++ b/third_party/WebKit/Source/core/css/resolver/AnimatedStyleBuilder.cpp
@@ -102,24 +102,31 @@
   return Length(Auto);
 }
 
-template <typename T>
-T animatableValueClampTo(const AnimatableValue* value,
-                         T min = defaultMinimumForClamp<T>(),
-                         T max = defaultMaximumForClamp<T>()) {
-  static_assert(std::is_integral<T>::value,
-                "should use integral type T when rounding values");
-  return clampTo<T>(
-      roundForImpreciseConversion<T>(toAnimatableDouble(value)->toDouble()),
-      min, max);
+double animatableValueToPixels(const AnimatableValue* value,
+                               const StyleResolverState& state) {
+  return toAnimatableLength(value)
+      ->getLength(state.style()->effectiveZoom(), ValueRangeAll)
+      .pixels();
 }
 
 template <typename T>
-T animatableLineWidthClamp(const AnimatableValue* value) {
-  double doubleValue = toAnimatableDouble(value)->toDouble();
+T roundedClampTo(double value) {
+  static_assert(std::is_integral<T>::value,
+                "should use integral type T when rounding values");
+  return clampTo<T>(roundForImpreciseConversion<T>(value));
+}
+
+template <typename T>
+T animatableValueClampTo(const AnimatableValue* value) {
+  return roundedClampTo<T>(toAnimatableDouble(value)->toDouble());
+}
+
+template <typename T>
+T animatableLineWidthClamp(const AnimatableValue* value,
+                           const StyleResolverState& state) {
+  double lineWidth = animatableValueToPixels(value, state);
   // This matches StyleBuilderConverter::convertLineWidth().
-  return (doubleValue > 0 && doubleValue < 1)
-             ? 1
-             : animatableValueClampTo<T>(value);
+  return (lineWidth > 0 && lineWidth < 1) ? 1 : roundedClampTo<T>(lineWidth);
 }
 
 LengthBox animatableValueToLengthBox(const AnimatableValue* value,
@@ -163,8 +170,7 @@
   return TransformOrigin(
       animatableValueToLength(animatableLengthPoint3D->x(), state),
       animatableValueToLength(animatableLengthPoint3D->y(), state),
-      clampTo<float>(
-          toAnimatableDouble(animatableLengthPoint3D->z())->toDouble()));
+      animatableValueToPixels(animatableLengthPoint3D->z(), state));
 }
 
 LengthSize animatableValueToLengthSize(const AnimatableValue* value,
@@ -352,7 +358,8 @@
           animatableValueToLengthSize(value, state, ValueRangeNonNegative));
       return;
     case CSSPropertyBorderBottomWidth:
-      style->setBorderBottomWidth(animatableLineWidthClamp<unsigned>(value));
+      style->setBorderBottomWidth(
+          animatableLineWidthClamp<unsigned>(value, state));
       return;
     case CSSPropertyBorderImageOutset:
       style->setBorderImageOutset(
@@ -379,7 +386,8 @@
           toAnimatableColor(value)->visitedLinkColor());
       return;
     case CSSPropertyBorderLeftWidth:
-      style->setBorderLeftWidth(animatableLineWidthClamp<unsigned>(value));
+      style->setBorderLeftWidth(
+          animatableLineWidthClamp<unsigned>(value, state));
       return;
     case CSSPropertyBorderRightColor:
       style->setBorderRightColor(toAnimatableColor(value)->getColor());
@@ -387,7 +395,8 @@
           toAnimatableColor(value)->visitedLinkColor());
       return;
     case CSSPropertyBorderRightWidth:
-      style->setBorderRightWidth(animatableLineWidthClamp<unsigned>(value));
+      style->setBorderRightWidth(
+          animatableLineWidthClamp<unsigned>(value, state));
       return;
     case CSSPropertyBorderTopColor:
       style->setBorderTopColor(toAnimatableColor(value)->getColor());
@@ -403,7 +412,8 @@
           animatableValueToLengthSize(value, state, ValueRangeNonNegative));
       return;
     case CSSPropertyBorderTopWidth:
-      style->setBorderTopWidth(animatableLineWidthClamp<unsigned>(value));
+      style->setBorderTopWidth(
+          animatableLineWidthClamp<unsigned>(value, state));
       return;
     case CSSPropertyBottom:
       style->setBottom(animatableValueToLength(value, state));
@@ -488,7 +498,7 @@
       return;
     case CSSPropertyLetterSpacing:
       style->setLetterSpacing(
-          clampTo<float>(toAnimatableDouble(value)->toDouble()));
+          clampTo<float>(animatableValueToPixels(value, state)));
       return;
     case CSSPropertyMarginBottom:
       style->setMarginBottom(animatableValueToLength(value, state));
@@ -536,10 +546,12 @@
           toAnimatableColor(value)->visitedLinkColor());
       return;
     case CSSPropertyOutlineOffset:
-      style->setOutlineOffset(animatableValueClampTo<int>(value));
+      style->setOutlineOffset(
+          roundedClampTo<int>(animatableValueToPixels(value, state)));
       return;
     case CSSPropertyOutlineWidth:
-      style->setOutlineWidth(animatableLineWidthClamp<unsigned short>(value));
+      style->setOutlineWidth(
+          animatableLineWidthClamp<unsigned short>(value, state));
       return;
     case CSSPropertyPaddingBottom:
       style->setPaddingBottom(
@@ -612,12 +624,12 @@
       style->setTop(animatableValueToLength(value, state));
       return;
     case CSSPropertyWebkitBorderHorizontalSpacing:
-      style->setHorizontalBorderSpacing(
-          animatableValueClampTo<unsigned short>(value));
+      style->setHorizontalBorderSpacing(roundedClampTo<unsigned short>(
+          animatableValueToPixels(value, state)));
       return;
     case CSSPropertyWebkitBorderVerticalSpacing:
-      style->setVerticalBorderSpacing(
-          animatableValueClampTo<unsigned short>(value));
+      style->setVerticalBorderSpacing(roundedClampTo<unsigned short>(
+          animatableValueToPixels(value, state)));
       return;
     case CSSPropertyClipPath:
       style->setClipPath(
@@ -628,7 +640,7 @@
           round(toAnimatableDouble(value)->toDouble()), 1));
       return;
     case CSSPropertyColumnGap:
-      style->setColumnGap(clampTo(toAnimatableDouble(value)->toDouble(), 0));
+      style->setColumnGap(clampTo(animatableValueToPixels(value, state), 0));
       return;
     case CSSPropertyColumnRuleColor:
       style->setColumnRuleColor(toAnimatableColor(value)->getColor());
@@ -636,12 +648,12 @@
           toAnimatableColor(value)->visitedLinkColor());
       return;
     case CSSPropertyColumnWidth:
-      style->setColumnWidth(clampTo(toAnimatableDouble(value)->toDouble(),
+      style->setColumnWidth(clampTo(animatableValueToPixels(value, state),
                                     std::numeric_limits<float>::epsilon()));
       return;
     case CSSPropertyColumnRuleWidth:
       style->setColumnRuleWidth(
-          animatableLineWidthClamp<unsigned short>(value));
+          animatableLineWidthClamp<unsigned short>(value, state));
       return;
     case CSSPropertyFilter:
       style->setFilter(toAnimatableFilterOperations(value)->operations());
@@ -687,8 +699,8 @@
       return;
     case CSSPropertyPerspective:
       style->setPerspective(
-          value->isDouble()
-              ? clampTo<float>(toAnimatableDouble(value)->toDouble())
+          value->isLength()
+              ? clampTo<float>(animatableValueToPixels(value, state))
               : 0);
       return;
     case CSSPropertyPerspectiveOrigin:
@@ -782,7 +794,7 @@
       style->setTransformOriginY(animatableValueToLength(value, state));
       return;
     case CSSPropertyWebkitTransformOriginZ:
-      style->setTransformOriginZ(toAnimatableDouble(value)->toDouble());
+      style->setTransformOriginZ(animatableValueToPixels(value, state));
       return;
     case CSSPropertyWidows:
       style->setWidows(
@@ -794,7 +806,7 @@
       return;
     case CSSPropertyWordSpacing:
       style->setWordSpacing(
-          clampTo<float>(toAnimatableDouble(value)->toDouble()));
+          clampTo<float>(animatableValueToPixels(value, state)));
       return;
     case CSSPropertyVerticalAlign:
       style->setVerticalAlignLength(animatableValueToLength(value, state));
diff --git a/third_party/WebKit/Source/core/editing/Editor.cpp b/third_party/WebKit/Source/core/editing/Editor.cpp
index a8b6dff..82343bab 100644
--- a/third_party/WebKit/Source/core/editing/Editor.cpp
+++ b/third_party/WebKit/Source/core/editing/Editor.cpp
@@ -1665,6 +1665,14 @@
   // TODO(tkent): Should we check and move Text node children of <html>?
 }
 
+void Editor::replaceSelection(const String& text) {
+  DCHECK(!frame().document()->needsLayoutTreeUpdate());
+  bool selectReplacement = behavior().shouldSelectReplacement();
+  bool smartReplace = true;
+  replaceSelectionWithText(text, selectReplacement, smartReplace,
+                           InputEvent::InputType::InsertReplacementText);
+}
+
 DEFINE_TRACE(Editor) {
   visitor->trace(m_frame);
   visitor->trace(m_lastEditCommand);
diff --git a/third_party/WebKit/Source/core/editing/Editor.h b/third_party/WebKit/Source/core/editing/Editor.h
index 41a5b49a..eedc2c7 100644
--- a/third_party/WebKit/Source/core/editing/Editor.h
+++ b/third_party/WebKit/Source/core/editing/Editor.h
@@ -261,7 +261,9 @@
                                 bool smartReplace,
                                 InputEvent::InputType);
 
-  // TODO(xiaochengh): Replace |bool| parameters by |enum|.
+  // Implementation of WebLocalFrameImpl::replaceSelection.
+  void replaceSelection(const String&);
+
   void replaceSelectionAfterDragging(DocumentFragment*,
                                      InsertMode,
                                      DragSourceType);
diff --git a/third_party/WebKit/Source/core/editing/spellcheck/SpellChecker.cpp b/third_party/WebKit/Source/core/editing/spellcheck/SpellChecker.cpp
index 94d2d7cc..56fb4484 100644
--- a/third_party/WebKit/Source/core/editing/spellcheck/SpellChecker.cpp
+++ b/third_party/WebKit/Source/core/editing/spellcheck/SpellChecker.cpp
@@ -1039,14 +1039,6 @@
   m_spellCheckRequester->cancelCheck();
 }
 
-void SpellChecker::requestTextChecking(const Element& element) {
-  if (!element.isSpellCheckingEnabled())
-    return;
-  const EphemeralRange rangeToCheck = EphemeralRange::rangeOfContents(element);
-  m_spellCheckRequester->requestCheckingFor(
-      SpellCheckRequest::create(rangeToCheck));
-}
-
 DEFINE_TRACE(SpellChecker) {
   visitor->trace(m_frame);
   visitor->trace(m_spellCheckRequester);
diff --git a/third_party/WebKit/Source/core/editing/spellcheck/SpellChecker.h b/third_party/WebKit/Source/core/editing/spellcheck/SpellChecker.h
index 952fed7..2f1d8eb2 100644
--- a/third_party/WebKit/Source/core/editing/spellcheck/SpellChecker.h
+++ b/third_party/WebKit/Source/core/editing/spellcheck/SpellChecker.h
@@ -83,7 +83,6 @@
   void updateMarkersForWordsAffectedByEditing(
       bool onlyHandleWordsContainingSelection);
   void cancelCheck();
-  void requestTextChecking(const Element&);
 
   // Exposed for testing only
   SpellCheckRequester& spellCheckRequester() const {
diff --git a/third_party/WebKit/Source/core/frame/History.cpp b/third_party/WebKit/Source/core/frame/History.cpp
index 470ca160..56494e9 100644
--- a/third_party/WebKit/Source/core/frame/History.cpp
+++ b/third_party/WebKit/Source/core/frame/History.cpp
@@ -145,14 +145,19 @@
   if (!NavigationDisablerForUnload::isNavigationAllowed())
     return;
 
-  // We intentionally call reload() for the current frame if delta is zero.
-  // Otherwise, navigation happens on the root frame.
-  // This behavior is designed in the following spec.
-  // https://html.spec.whatwg.org/multipage/browsers.html#dom-history-go
-  if (delta)
+  if (delta) {
     frame()->loader().client()->navigateBackForward(delta);
-  else
-    frame()->reload(FrameLoadTypeReload, ClientRedirectPolicy::ClientRedirect);
+  } else {
+    // We intentionally call reload() for the current frame if delta is zero.
+    // Otherwise, navigation happens on the root frame.
+    // This behavior is designed in the following spec.
+    // https://html.spec.whatwg.org/multipage/browsers.html#dom-history-go
+    FrameLoadType reloadType =
+        RuntimeEnabledFeatures::fasterLocationReloadEnabled()
+            ? FrameLoadTypeReloadMainResource
+            : FrameLoadTypeReload;
+    frame()->reload(reloadType, ClientRedirectPolicy::ClientRedirect);
+  }
 }
 
 KURL History::urlForState(const String& urlString) {
diff --git a/third_party/WebKit/Source/core/frame/LocalFrame.cpp b/third_party/WebKit/Source/core/frame/LocalFrame.cpp
index c5b21581..67220a3 100644
--- a/third_party/WebKit/Source/core/frame/LocalFrame.cpp
+++ b/third_party/WebKit/Source/core/frame/LocalFrame.cpp
@@ -377,7 +377,10 @@
     request.setClientRedirect(clientRedirectPolicy);
     m_loader.load(request, loadType);
   } else {
-    DCHECK_EQ(FrameLoadTypeReload, loadType);
+    if (RuntimeEnabledFeatures::fasterLocationReloadEnabled())
+      DCHECK_EQ(FrameLoadTypeReloadMainResource, loadType);
+    else
+      DCHECK_EQ(FrameLoadTypeReload, loadType);
     m_navigationScheduler->scheduleReload();
   }
 }
diff --git a/third_party/WebKit/Source/core/frame/Location.cpp b/third_party/WebKit/Source/core/frame/Location.cpp
index dd3626c..37a04e2b 100644
--- a/third_party/WebKit/Source/core/frame/Location.cpp
+++ b/third_party/WebKit/Source/core/frame/Location.cpp
@@ -248,7 +248,11 @@
     return;
   if (protocolIsJavaScript(toLocalFrame(m_frame)->document()->url()))
     return;
-  m_frame->reload(FrameLoadTypeReload, ClientRedirectPolicy::ClientRedirect);
+  FrameLoadType reloadType =
+      RuntimeEnabledFeatures::fasterLocationReloadEnabled()
+          ? FrameLoadTypeReloadMainResource
+          : FrameLoadTypeReload;
+  m_frame->reload(reloadType, ClientRedirectPolicy::ClientRedirect);
 }
 
 void Location::setLocation(const String& url,
diff --git a/third_party/WebKit/Source/core/html/HTMLLinkElement.cpp b/third_party/WebKit/Source/core/html/HTMLLinkElement.cpp
index e3baf91..ae4f4dd 100644
--- a/third_party/WebKit/Source/core/html/HTMLLinkElement.cpp
+++ b/third_party/WebKit/Source/core/html/HTMLLinkElement.cpp
@@ -324,7 +324,10 @@
 }
 
 KURL HTMLLinkElement::href() const {
-  return document().completeURL(getAttribute(hrefAttr));
+  const String& url = getAttribute(hrefAttr);
+  if (url.isEmpty())
+    return KURL();
+  return document().completeURL(url);
 }
 
 const AtomicString& HTMLLinkElement::rel() const {
diff --git a/third_party/WebKit/Source/core/html/HTMLLinkElementTest.cpp b/third_party/WebKit/Source/core/html/HTMLLinkElementTest.cpp
new file mode 100644
index 0000000..e73be25
--- /dev/null
+++ b/third_party/WebKit/Source/core/html/HTMLLinkElementTest.cpp
@@ -0,0 +1,41 @@
+// 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.
+
+#include "core/html/HTMLLinkElement.h"
+
+#include "core/dom/Document.h"
+#include "core/frame/FrameView.h"
+#include "core/html/HTMLHeadElement.h"
+#include "core/testing/DummyPageHolder.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace blink {
+
+class HTMLLinkElementTest : public ::testing::Test {
+ protected:
+  void SetUp() override;
+  Document& document() const { return m_dummyPageHolder->document(); }
+
+ private:
+  std::unique_ptr<DummyPageHolder> m_dummyPageHolder;
+};
+
+void HTMLLinkElementTest::SetUp() {
+  m_dummyPageHolder = DummyPageHolder::create(IntSize(800, 600));
+}
+
+// This tests that we should ignore empty string value
+// in href attribute value of the link element.
+TEST_F(HTMLLinkElementTest, EmptyHrefAttribute) {
+  document().documentElement()->setInnerHTML(
+      "<head>"
+      "<link rel=\"icon\" type=\"image/ico\" href=\"\" />"
+      "</head>");
+  HTMLLinkElement* linkElement =
+      toElement<HTMLLinkElement>(document().head()->firstChild());
+  EXPECT_EQ(KURL(), linkElement->href());
+}
+
+}  // namespace blink
diff --git a/third_party/WebKit/Source/core/loader/NavigationScheduler.cpp b/third_party/WebKit/Source/core/loader/NavigationScheduler.cpp
index 0abc6a5..a7740dee 100644
--- a/third_party/WebKit/Source/core/loader/NavigationScheduler.cpp
+++ b/third_party/WebKit/Source/core/loader/NavigationScheduler.cpp
@@ -271,7 +271,10 @@
     request.setClientRedirect(ClientRedirectPolicy::ClientRedirect);
     maybeLogScheduledNavigationClobber(ScheduledNavigationType::ScheduledReload,
                                        frame);
-    frame->loader().load(request, FrameLoadTypeReload);
+    if (RuntimeEnabledFeatures::fasterLocationReloadEnabled())
+      frame->loader().load(request, FrameLoadTypeReloadMainResource);
+    else
+      frame->loader().load(request, FrameLoadTypeReload);
   }
 
  private:
diff --git a/third_party/WebKit/Source/core/workers/DedicatedWorkerTest.cpp b/third_party/WebKit/Source/core/workers/DedicatedWorkerTest.cpp
index 85b1f075..f499ed5 100644
--- a/third_party/WebKit/Source/core/workers/DedicatedWorkerTest.cpp
+++ b/third_party/WebKit/Source/core/workers/DedicatedWorkerTest.cpp
@@ -3,11 +3,13 @@
 // found in the LICENSE file.
 
 #include "core/events/MessageEvent.h"
+#include "core/inspector/ConsoleMessageStorage.h"
 #include "core/testing/DummyPageHolder.h"
 #include "core/workers/DedicatedWorkerGlobalScope.h"
 #include "core/workers/DedicatedWorkerThread.h"
 #include "core/workers/InProcessWorkerMessagingProxy.h"
 #include "core/workers/InProcessWorkerObjectProxy.h"
+#include "core/workers/WorkerInspectorProxy.h"
 #include "core/workers/WorkerThread.h"
 #include "core/workers/WorkerThreadStartupData.h"
 #include "core/workers/WorkerThreadTestHelper.h"
@@ -52,6 +54,33 @@
         std::move(startupData->m_starterOriginPrivilegeData),
         std::move(startupData->m_workerClients));
   }
+
+  // Emulates API use on DedicatedWorkerGlobalScope.
+  void countFeature(UseCounter::Feature feature) {
+    EXPECT_TRUE(isCurrentThread());
+    globalScope()->countFeature(feature);
+    workerReportingProxy()
+        .getParentFrameTaskRunners()
+        ->get(TaskType::Internal)
+        ->postTask(BLINK_FROM_HERE, crossThreadBind(&testing::exitRunLoop));
+  }
+
+  // Emulates deprecated API use on DedicatedWorkerGlobalScope.
+  void countDeprecation(UseCounter::Feature feature) {
+    EXPECT_TRUE(isCurrentThread());
+    EXPECT_EQ(0u, consoleMessageStorage()->size());
+    globalScope()->countDeprecation(feature);
+
+    // countDeprecation() should add a warning message.
+    EXPECT_EQ(1u, consoleMessageStorage()->size());
+    String consoleMessage = consoleMessageStorage()->at(0)->message();
+    EXPECT_TRUE(consoleMessage.contains("deprecated"));
+
+    workerReportingProxy()
+        .getParentFrameTaskRunners()
+        ->get(TaskType::Internal)
+        ->postTask(BLINK_FROM_HERE, crossThreadBind(&testing::exitRunLoop));
+  }
 };
 
 class InProcessWorkerMessagingProxyForTest
@@ -71,8 +100,6 @@
     m_workerThread = WTF::wrapUnique(
         new DedicatedWorkerThreadForTest(m_mockWorkerLoaderProxyProvider.get(),
                                          workerObjectProxy(), threadHeapMode));
-    workerThreadCreated();
-
     m_mockWorkerThreadLifecycleObserver = new MockWorkerThreadLifecycleObserver(
         m_workerThread->getWorkerThreadLifecycleContext());
     EXPECT_CALL(*m_mockWorkerThreadLifecycleObserver, contextDestroyed())
@@ -85,6 +112,27 @@
         m_mockWorkerLoaderProxyProvider.get());
   }
 
+  void startWithSourceCode(const String& source) {
+    KURL scriptURL(ParsedURLString, "http://fake.url/");
+    m_securityOrigin = SecurityOrigin::create(scriptURL);
+    std::unique_ptr<Vector<CSPHeaderAndType>> headers =
+        WTF::makeUnique<Vector<CSPHeaderAndType>>();
+    CSPHeaderAndType headerAndType("contentSecurityPolicy",
+                                   ContentSecurityPolicyHeaderTypeReport);
+    headers->append(headerAndType);
+    workerThread()->start(WorkerThreadStartupData::create(
+        scriptURL, "fake user agent", source, nullptr /* cachedMetaData */,
+        DontPauseWorkerGlobalScopeOnStart, headers.get(),
+        "" /* referrerPolicy */, m_securityOrigin.get(),
+        nullptr /* workerClients */, WebAddressSpaceLocal,
+        nullptr /* originTrialTokens */, nullptr /* workerSettings */,
+        V8CacheOptionsDefault));
+
+    workerInspectorProxy()->workerThreadCreated(
+        toDocument(getExecutionContext()), m_workerThread.get(), scriptURL);
+    workerThreadCreated();
+  }
+
   enum class Notification {
     MessageConfirmed,
     PendingActivityReported,
@@ -145,6 +193,7 @@
       m_mockWorkerLoaderProxyProvider;
   Persistent<MockWorkerThreadLifecycleObserver>
       m_mockWorkerThreadLifecycleObserver;
+  RefPtr<SecurityOrigin> m_securityOrigin;
 
   WTF::Deque<Notification> m_events;
   bool m_blocking = false;
@@ -162,8 +211,6 @@
     m_workerMessagingProxy =
         WTF::wrapUnique(new InProcessWorkerMessagingProxyForTest(
             &m_page->document(), m_threadHeapMode));
-    m_securityOrigin =
-        SecurityOrigin::create(KURL(ParsedURLString, "http://fake.url/"));
   }
 
   void TearDown() override {
@@ -172,21 +219,6 @@
               workerMessagingProxy()->waitForNotification());
   }
 
-  void startWithSourceCode(const String& source) {
-    std::unique_ptr<Vector<CSPHeaderAndType>> headers =
-        WTF::makeUnique<Vector<CSPHeaderAndType>>();
-    CSPHeaderAndType headerAndType("contentSecurityPolicy",
-                                   ContentSecurityPolicyHeaderTypeReport);
-    headers->append(headerAndType);
-    workerThread()->start(WorkerThreadStartupData::create(
-        KURL(ParsedURLString, "http://fake.url/"), "fake user agent", source,
-        nullptr /* cachedMetaData */, DontPauseWorkerGlobalScopeOnStart,
-        headers.get(), "" /* referrerPolicy */, m_securityOrigin.get(),
-        nullptr /* workerClients */, WebAddressSpaceLocal,
-        nullptr /* originTrialTokens */, nullptr /* workerSettings */,
-        V8CacheOptionsDefault));
-  }
-
   void dispatchMessageEvent() {
     workerMessagingProxy()->postMessageToWorkerGlobalScope(
         nullptr /* message */, nullptr /* channels */);
@@ -200,8 +232,9 @@
     return m_workerMessagingProxy->workerThread();
   }
 
+  Document& document() { return m_page->document(); }
+
  private:
-  RefPtr<SecurityOrigin> m_securityOrigin;
   std::unique_ptr<DummyPageHolder> m_page;
   std::unique_ptr<InProcessWorkerMessagingProxyForTest> m_workerMessagingProxy;
   const BlinkGC::ThreadHeapMode m_threadHeapMode;
@@ -217,7 +250,7 @@
 
 TEST_P(DedicatedWorkerTest, PendingActivity_NoActivity) {
   const String sourceCode = "// Do nothing";
-  startWithSourceCode(sourceCode);
+  workerMessagingProxy()->startWithSourceCode(sourceCode);
 
   // Worker initialization should be counted as a pending activity.
   EXPECT_TRUE(workerMessagingProxy()->hasPendingActivity());
@@ -231,7 +264,7 @@
 TEST_P(DedicatedWorkerTest, PendingActivity_SetTimeout) {
   // Start an oneshot timer on initial script evaluation.
   const String sourceCode = "setTimeout(function() {}, 0);";
-  startWithSourceCode(sourceCode);
+  workerMessagingProxy()->startWithSourceCode(sourceCode);
 
   // Worker initialization should be counted as a pending activity.
   EXPECT_TRUE(workerMessagingProxy()->hasPendingActivity());
@@ -250,7 +283,7 @@
   const String sourceCode =
       "var id = setInterval(function() {}, 50);"
       "addEventListener('message', function(event) { clearInterval(id); });";
-  startWithSourceCode(sourceCode);
+  workerMessagingProxy()->startWithSourceCode(sourceCode);
 
   // Worker initialization should be counted as a pending activity.
   EXPECT_TRUE(workerMessagingProxy()->hasPendingActivity());
@@ -276,7 +309,7 @@
       "addEventListener('message', function(event) {"
       "  setTimeout(function() {}, 0);"
       "});";
-  startWithSourceCode(sourceCode);
+  workerMessagingProxy()->startWithSourceCode(sourceCode);
 
   // Worker initialization should be counted as a pending activity.
   EXPECT_TRUE(workerMessagingProxy()->hasPendingActivity());
@@ -314,7 +347,7 @@
       "    clearInterval(id);"
       "  }"
       "});";
-  startWithSourceCode(sourceCode);
+  workerMessagingProxy()->startWithSourceCode(sourceCode);
 
   // Worker initialization should be counted as a pending activity.
   EXPECT_TRUE(workerMessagingProxy()->hasPendingActivity());
@@ -354,4 +387,35 @@
   EXPECT_FALSE(workerMessagingProxy()->hasPendingActivity());
 }
 
+TEST_P(DedicatedWorkerTest, UseCounter) {
+  const String sourceCode = "// Do nothing";
+  workerMessagingProxy()->startWithSourceCode(sourceCode);
+
+  // This feature is randomly selected.
+  const UseCounter::Feature feature1 = UseCounter::Feature::RequestFileSystem;
+
+  // API use on the DedicatedWorkerGlobalScope should be recorded in UseCounter
+  // on the Document.
+  EXPECT_FALSE(UseCounter::isCounted(document(), feature1));
+  workerThread()->postTask(
+      BLINK_FROM_HERE,
+      createCrossThreadTask(&DedicatedWorkerThreadForTest::countFeature,
+                            crossThreadUnretained(workerThread()), feature1));
+  testing::enterRunLoop();
+  EXPECT_TRUE(UseCounter::isCounted(document(), feature1));
+
+  // This feature is randomly selected from Deprecation::deprecationMessage().
+  const UseCounter::Feature feature2 = UseCounter::Feature::PrefixedStorageInfo;
+
+  // Deprecated API use on the DedicatedWorkerGlobalScope should be recorded in
+  // UseCounter on the Document.
+  EXPECT_FALSE(UseCounter::isCounted(document(), feature2));
+  workerThread()->postTask(
+      BLINK_FROM_HERE,
+      createCrossThreadTask(&DedicatedWorkerThreadForTest::countDeprecation,
+                            crossThreadUnretained(workerThread()), feature2));
+  testing::enterRunLoop();
+  EXPECT_TRUE(UseCounter::isCounted(document(), feature2));
+}
+
 }  // namespace blink
diff --git a/third_party/WebKit/Source/devtools/.eslintrc.js b/third_party/WebKit/Source/devtools/.eslintrc.js
index 1e9bf34..914281a 100644
--- a/third_party/WebKit/Source/devtools/.eslintrc.js
+++ b/third_party/WebKit/Source/devtools/.eslintrc.js
@@ -63,6 +63,7 @@
         "no-unsafe-negation": 2,
         "radix": 2,
         "valid-typeof": 2,
+        "no-unused-vars": [2, { "args": "none", "vars": "local" }],
 
         // es2015 features
         "require-yield": 2,
diff --git a/third_party/WebKit/Source/devtools/BUILD.gn b/third_party/WebKit/Source/devtools/BUILD.gn
index b9ea6471..71a4868 100644
--- a/third_party/WebKit/Source/devtools/BUILD.gn
+++ b/third_party/WebKit/Source/devtools/BUILD.gn
@@ -113,7 +113,6 @@
   "front_end/sdk/CSSMedia.js",
   "front_end/sdk/CSSMetadata.js",
   "front_end/sdk/CSSModel.js",
-  "front_end/sdk/CSSParser.js",
   "front_end/sdk/CSSProperty.js",
   "front_end/sdk/CSSRule.js",
   "front_end/sdk/CSSStyleDeclaration.js",
diff --git a/third_party/WebKit/Source/devtools/front_end/Tests.js b/third_party/WebKit/Source/devtools/front_end/Tests.js
index 62714a8..5e94b31 100644
--- a/third_party/WebKit/Source/devtools/front_end/Tests.js
+++ b/third_party/WebKit/Source/devtools/front_end/Tests.js
@@ -302,12 +302,6 @@
   TestSuite.prototype.testNoScriptDuplicatesOnPanelSwitch = function() {
     var test = this;
 
-    // There should be two scripts: one for the main page and another
-    // one which is source of console API(see
-    // InjectedScript._ensureCommandLineAPIInstalled).
-    var expectedScriptsCount = 2;
-    var parsedScripts = [];
-
     function switchToElementsTab() {
       test.showPanel('elements').then(function() {
         setTimeout(switchToScriptsTab, 0);
@@ -573,7 +567,6 @@
     this._waitForTargets(2, callback.bind(this));
 
     function callback() {
-      var target = SDK.targetManager.targets(SDK.Target.Capability.JS)[0];
       InspectorBackendClass.deprecatedRunAfterPendingDispatches(this.releaseControl.bind(this));
     }
   };
diff --git a/third_party/WebKit/Source/devtools/front_end/accessibility/AccessibilityNodeView.js b/third_party/WebKit/Source/devtools/front_end/accessibility/AccessibilityNodeView.js
index 3805398..88396b1 100644
--- a/third_party/WebKit/Source/devtools/front_end/accessibility/AccessibilityNodeView.js
+++ b/third_party/WebKit/Source/devtools/front_end/accessibility/AccessibilityNodeView.js
@@ -347,8 +347,6 @@
    */
   appendIDRefValueElement(value) {
     var relatedNodes = value.relatedNodes;
-    var numNodes = relatedNodes.length;
-    var valueElement;
 
     var idrefs = value.value.trim().split(/\s+/);
     if (idrefs.length === 1) {
@@ -400,7 +398,6 @@
     var nameElement = createElement('span');
     var AXValueSourceType = Protocol.Accessibility.AXValueSourceType;
     var type = source.type;
-    var name;
     switch (type) {
       case AXValueSourceType.Attribute:
       case AXValueSourceType.Placeholder:
diff --git a/third_party/WebKit/Source/devtools/front_end/animation/AnimationUI.js b/third_party/WebKit/Source/devtools/front_end/animation/AnimationUI.js
index 45c161f..816766e3 100644
--- a/third_party/WebKit/Source/devtools/front_end/animation/AnimationUI.js
+++ b/third_party/WebKit/Source/devtools/front_end/animation/AnimationUI.js
@@ -203,8 +203,6 @@
   }
 
   redraw() {
-    var durationWithDelay =
-        this._delay() + this._duration() * this._animation.source().iterations() + this._animation.source().endDelay();
     var maxWidth = this._timeline.width() - Animation.AnimationUI.Options.AnimationMargin;
 
     this._svg.setAttribute('width', (maxWidth + 2 * Animation.AnimationUI.Options.AnimationMargin).toFixed(2));
diff --git a/third_party/WebKit/Source/devtools/front_end/audits/AuditRules.js b/third_party/WebKit/Source/devtools/front_end/audits/AuditRules.js
index 8aec3f3..78e2f4df1 100644
--- a/third_party/WebKit/Source/devtools/front_end/audits/AuditRules.js
+++ b/third_party/WebKit/Source/devtools/front_end/audits/AuditRules.js
@@ -84,8 +84,6 @@
    */
   doRun(target, requests, result, callback, progress) {
     var totalSavings = 0;
-    var compressedSize = 0;
-    var candidateSize = 0;
     var summary = result.addChild('', true);
     for (var i = 0, length = requests.length; i < length; ++i) {
       var request = requests[i];
@@ -93,11 +91,8 @@
         continue;  // Do not test cached resources.
       if (this._shouldCompress(request)) {
         var size = request.resourceSize;
-        candidateSize += size;
-        if (this._isCompressed(request)) {
-          compressedSize += size;
+        if (this._isCompressed(request))
           continue;
-        }
         var savings = 2 * size / 3;
         totalSavings += savings;
         summary.addFormatted('%r could save ~%s', request.url, Number.bytesToString(savings));
@@ -510,29 +505,30 @@
   }
 
   run() {
-    this._parser = new SDK.CSSParser();
     this._processNextStyleSheet();
   }
 
-  _terminateWorker() {
-    if (this._parser) {
-      this._parser.dispose();
-      delete this._parser;
-    }
-  }
-
-  _finish() {
-    this._terminateWorker();
-    this._styleSheetsParsedCallback(this._styleSheets);
-  }
-
   _processNextStyleSheet() {
     if (!this._styleSheetHeaders.length) {
-      this._finish();
+      this._styleSheetsParsedCallback(this._styleSheets);
       return;
     }
     this._currentStyleSheetHeader = this._styleSheetHeaders.shift();
-    this._parser.fetchAndParse(this._currentStyleSheetHeader, this._onStyleSheetParsed.bind(this));
+
+    var allRules = [];
+    this._currentStyleSheetHeader.requestContent().then(
+        content => Common.formatterWorkerPool.parseCSS(content || '', onRulesParsed.bind(this)));
+
+    /**
+     * @param {boolean} isLastChunk
+     * @param {!Array<!Common.FormatterWorkerPool.CSSRule>} rules
+     * @this {Audits.AuditRules.StyleSheetProcessor}
+     */
+    function onRulesParsed(isLastChunk, rules) {
+      allRules.push(...rules);
+      if (isLastChunk)
+        this._onStyleSheetParsed(allRules);
+    }
   }
 
   /**
@@ -540,7 +536,7 @@
    */
   _onStyleSheetParsed(rules) {
     if (this._progress.isCanceled()) {
-      this._finish();
+      this._styleSheetsParsedCallback(this._styleSheets);
       return;
     }
 
diff --git a/third_party/WebKit/Source/devtools/front_end/bindings/TempFile.js b/third_party/WebKit/Source/devtools/front_end/bindings/TempFile.js
index ed0573d..a402a168 100644
--- a/third_party/WebKit/Source/devtools/front_end/bindings/TempFile.js
+++ b/third_party/WebKit/Source/devtools/front_end/bindings/TempFile.js
@@ -463,6 +463,8 @@
       this._fileSize = fileSize;
     }
 
+    if (!this._file)
+      this._file = new Bindings.DeferredTempFile(this._dirName, String(Date.now()));
     this._file.write(this._strings, didWrite.bind(this, chunk));
     this._strings = [];
     this._stringsLength = 0;
@@ -483,7 +485,7 @@
   reset() {
     if (this._file)
       this._file.remove();
-    this._file = new Bindings.DeferredTempFile(this._dirName, String(Date.now()));
+    this._file = null;
     /**
      * @type {!Array.<string>}
      */
@@ -497,7 +499,8 @@
    * @param {!Bindings.OutputStreamDelegate} delegate
    */
   writeToStream(outputStream, delegate) {
-    this._file.copyToOutputStream(outputStream, delegate);
+    if (this._file)
+      this._file.copyToOutputStream(outputStream, delegate);
   }
 };
 
diff --git a/third_party/WebKit/Source/devtools/front_end/common/Object.js b/third_party/WebKit/Source/devtools/front_end/common/Object.js
index c25bcda9..cf05394 100644
--- a/third_party/WebKit/Source/devtools/front_end/common/Object.js
+++ b/third_party/WebKit/Source/devtools/front_end/common/Object.js
@@ -69,13 +69,6 @@
 
   /**
    * @override
-   */
-  removeAllListeners() {
-    delete this._listeners;
-  }
-
-  /**
-   * @override
    * @param {symbol} eventType
    * @return {boolean}
    */
@@ -144,8 +137,6 @@
    */
   removeEventListener(eventType, listener, thisObject) {},
 
-  removeAllListeners() {},
-
   /**
    * @param {symbol} eventType
    * @return {boolean}
diff --git a/third_party/WebKit/Source/devtools/front_end/components/Spectrum.js b/third_party/WebKit/Source/devtools/front_end/components/Spectrum.js
index c714b4a8..1978f6c 100644
--- a/third_party/WebKit/Source/devtools/front_end/components/Spectrum.js
+++ b/third_party/WebKit/Source/devtools/front_end/components/Spectrum.js
@@ -277,9 +277,6 @@
     }
     this._paletteContainerMutable = palette.mutable;
 
-    var numItems = palette.colors.length;
-    if (palette.mutable)
-      numItems++;
     if (palette.mutable) {
       this._paletteContainer.appendChild(this._addColorToolbar.element);
       this._paletteContainer.appendChild(this._deleteIconToolbar.element);
diff --git a/third_party/WebKit/Source/devtools/front_end/console/ConsoleViewMessage.js b/third_party/WebKit/Source/devtools/front_end/console/ConsoleViewMessage.js
index 437a4ed..8bc8645 100644
--- a/third_party/WebKit/Source/devtools/front_end/console/ConsoleViewMessage.js
+++ b/third_party/WebKit/Source/devtools/front_end/console/ConsoleViewMessage.js
@@ -312,7 +312,7 @@
     var contentElement = toggleElement.createChild('div', 'console-message-stack-trace-wrapper');
 
     var messageElement = this._buildMessage(consoleMessage);
-    var icon = UI.Icon.create('smallicon-triangle-right', 'stack-trace-expand-icon');
+    var icon = UI.Icon.create('smallicon-triangle-right');
     var clickableElement = contentElement.createChild('div');
     clickableElement.appendChild(icon);
 
diff --git a/third_party/WebKit/Source/devtools/front_end/console/consoleView.css b/third_party/WebKit/Source/devtools/front_end/console/consoleView.css
index 6d7e9f8b..106de85 100644
--- a/third_party/WebKit/Source/devtools/front_end/console/consoleView.css
+++ b/third_party/WebKit/Source/devtools/front_end/console/consoleView.css
@@ -366,7 +366,3 @@
 .console-message-stack-trace-wrapper > * {
     flex: none;
 }
-
-.stack-trace-expand-icon {
-    background-color: rgb(110, 110, 110);
-}
diff --git a/third_party/WebKit/Source/devtools/front_end/elements/ElementsTreeElement.js b/third_party/WebKit/Source/devtools/front_end/elements/ElementsTreeElement.js
index 4b037003..56309c5 100644
--- a/third_party/WebKit/Source/devtools/front_end/elements/ElementsTreeElement.js
+++ b/third_party/WebKit/Source/devtools/front_end/elements/ElementsTreeElement.js
@@ -478,7 +478,6 @@
 
   populateNodeContextMenu(contextMenu) {
     // Add free-form node-related actions.
-    var openTagElement = this._node[this.treeOutline.treeElementSymbol()] || this;
     var isEditable = this.hasEditableNode();
     if (isEditable && !this._editing)
       contextMenu.appendItem(Common.UIString('Edit as HTML'), this._editAsHTML.bind(this));
diff --git a/third_party/WebKit/Source/devtools/front_end/elements/EventListenersWidget.js b/third_party/WebKit/Source/devtools/front_end/elements/EventListenersWidget.js
index bd334a8..c76c16d8 100644
--- a/third_party/WebKit/Source/devtools/front_end/elements/EventListenersWidget.js
+++ b/third_party/WebKit/Source/devtools/front_end/elements/EventListenersWidget.js
@@ -100,7 +100,6 @@
     this._lastRequestedNode = node;
     var selectedNodeOnly = !this._showForAncestorsSetting.get();
     var promises = [];
-    var listenersView = this._eventListenersView;
     promises.push(node.resolveToObjectPromise(Elements.EventListenersWidget._objectGroupName));
     if (!selectedNodeOnly) {
       var currentNode = node.parentNode;
diff --git a/third_party/WebKit/Source/devtools/front_end/elements/StylesSidebarPane.js b/third_party/WebKit/Source/devtools/front_end/elements/StylesSidebarPane.js
index dd1b2fb..4d18ad3 100644
--- a/third_party/WebKit/Source/devtools/front_end/elements/StylesSidebarPane.js
+++ b/third_party/WebKit/Source/devtools/front_end/elements/StylesSidebarPane.js
@@ -1513,7 +1513,6 @@
     var oldSelectorRange = rule.selectorRange();
     if (!oldSelectorRange)
       return Promise.resolve();
-    var selectedNode = this._parentPane.node();
     return rule.setSelectorText(newContent)
         .then(onSelectorsUpdated.bind(this, /** @type {!SDK.CSSStyleRule} */ (rule), oldSelectorRange));
   }
@@ -1719,7 +1718,6 @@
     var oldRange = rule.key().range;
     if (!oldRange)
       return Promise.resolve();
-    var selectedNode = this._parentPane.node();
     return rule.setKeyText(newContent).then(updateSourceRanges.bind(this));
   }
 
@@ -2535,7 +2533,7 @@
     var isEditingName = context.isEditingName;
 
     // Determine where to move to before making changes
-    var createNewProperty, moveToPropertyName, moveToSelector;
+    var createNewProperty, moveToSelector;
     var isDataPasted = 'originalName' in context;
     var isDirtyViaPaste = isDataPasted && (this.nameElement.textContent !== context.originalName ||
                                            this.valueElement.textContent !== context.originalValue);
@@ -2546,12 +2544,12 @@
     if (moveDirection === 'forward' && (!isEditingName || isPropertySplitPaste) ||
         moveDirection === 'backward' && isEditingName) {
       moveTo = moveTo._findSibling(moveDirection);
-      if (moveTo)
-        moveToPropertyName = moveTo.name;
-      else if (moveDirection === 'forward' && (!this._newProperty || userInput))
-        createNewProperty = true;
-      else if (moveDirection === 'backward')
-        moveToSelector = true;
+      if (!moveTo) {
+        if (moveDirection === 'forward' && (!this._newProperty || userInput))
+          createNewProperty = true;
+        else if (moveDirection === 'backward')
+          moveToSelector = true;
+      }
     }
 
     // Make the Changes and trigger the moveToNextCallback after updating.
diff --git a/third_party/WebKit/Source/devtools/front_end/extensions/ExtensionAPI.js b/third_party/WebKit/Source/devtools/front_end/extensions/ExtensionAPI.js
index 12251be..0fb0077 100644
--- a/third_party/WebKit/Source/devtools/front_end/extensions/ExtensionAPI.js
+++ b/third_party/WebKit/Source/devtools/front_end/extensions/ExtensionAPI.js
@@ -67,6 +67,7 @@
     AddRequestHeaders: 'addRequestHeaders',
     AddTraceProvider: 'addTraceProvider',
     ApplyStyleSheet: 'applyStyleSheet',
+    CompleteTraceSession: 'completeTraceSession',
     CreatePanel: 'createPanel',
     CreateSidebarPane: 'createSidebarPane',
     CreateToolbarButton: 'createToolbarButton',
@@ -368,6 +369,7 @@
   var PanelWithSidebar = declareInterfaceClass(PanelWithSidebarImpl);
   var Request = declareInterfaceClass(RequestImpl);
   var Resource = declareInterfaceClass(ResourceImpl);
+  var TraceSession = declareInterfaceClass(TraceSessionImpl);
 
   /**
    * @constructor
@@ -505,16 +507,43 @@
    * @constructor
    * @param {string} id
    */
+  function TraceSessionImpl(id) {
+    this._id = id;
+  }
+
+  TraceSessionImpl.prototype =
+  {
+    /**
+     * @param {string=} url
+     * @param {number=} timeOffset
+     */
+    complete: function(url, timeOffset) {
+      var request = {command: commands.CompleteTraceSession, id: this._id, url: url || '', timeOffset: timeOffset || 0};
+      extensionServer.sendRequest(request);
+    }
+  };
+
+  /**
+   * @constructor
+   * @param {string} id
+   */
   function TraceProvider(id) {
-    this.onRecordingStarted = new EventSink(events.RecordingStarted + id);
+    /**
+     * @this {EventSinkImpl}
+     */
+    function dispatchRecordingStarted(message) {
+      var sessionId = message.arguments[0];
+      this._fire(new TraceSession(sessionId));
+    }
+
+    this.onRecordingStarted = new EventSink(events.RecordingStarted + id, dispatchRecordingStarted);
     this.onRecordingStopped = new EventSink(events.RecordingStopped + id);
   }
 
   /**
    * @constructor
    */
-  function Audits() {
-  }
+  function Audits() {}
 
   Audits.prototype = {
     /**
diff --git a/third_party/WebKit/Source/devtools/front_end/extensions/ExtensionServer.js b/third_party/WebKit/Source/devtools/front_end/extensions/ExtensionServer.js
index 1410c0ba..2511fdb2 100644
--- a/third_party/WebKit/Source/devtools/front_end/extensions/ExtensionServer.js
+++ b/third_party/WebKit/Source/devtools/front_end/extensions/ExtensionServer.js
@@ -47,12 +47,14 @@
     this._lastRequestId = 0;
     this._registeredExtensions = {};
     this._status = new Extensions.ExtensionStatus();
-    /** @type {!Array.<!Extensions.ExtensionSidebarPane>} */
+    /** @type {!Array<!Extensions.ExtensionSidebarPane>} */
     this._sidebarPanes = [];
-    /** @type {!Array.<!Extensions.ExtensionAuditCategory>} */
+    /** @type {!Array<!Extensions.ExtensionAuditCategory>} */
     this._auditCategories = [];
-    /** @type {!Array.<!Extensions.ExtensionTraceProvider>} */
+    /** @type {!Array<!Extensions.ExtensionTraceProvider>} */
     this._traceProviders = [];
+    /** @type {!Map<string, !Extensions.TracingSession>} */
+    this._traceSessions = new Map();
 
     var commands = Extensions.extensionAPI.Commands;
 
@@ -61,6 +63,7 @@
     this._registerHandler(commands.AddRequestHeaders, this._onAddRequestHeaders.bind(this));
     this._registerHandler(commands.AddTraceProvider, this._onAddTraceProvider.bind(this));
     this._registerHandler(commands.ApplyStyleSheet, this._onApplyStyleSheet.bind(this));
+    this._registerHandler(commands.CompleteTraceSession, this._onCompleteTraceSession.bind(this));
     this._registerHandler(commands.CreatePanel, this._onCreatePanel.bind(this));
     this._registerHandler(commands.CreateSidebarPane, this._onCreateSidebarPane.bind(this));
     this._registerHandler(commands.CreateToolbarButton, this._onCreateToolbarButton.bind(this));
@@ -164,17 +167,20 @@
   }
 
   /**
-   * @param {string} traceProviderId
+   * @param {string} providerId
+   * @param {string} sessionId
+   * @param {!Extensions.TracingSession} session
    */
-  startTraceRecording(traceProviderId) {
-    this._postNotification('trace-recording-started-' + traceProviderId);
+  startTraceRecording(providerId, sessionId, session) {
+    this._traceSessions.set(sessionId, session);
+    this._postNotification('trace-recording-started-' + providerId, sessionId);
   }
 
   /**
-   * @param {string} traceProviderId
+   * @param {string} providerId
    */
-  stopTraceRecording(traceProviderId) {
-    this._postNotification('trace-recording-stopped-' + traceProviderId);
+  stopTraceRecording(providerId) {
+    this._postNotification('trace-recording-stopped-' + providerId);
   }
 
   /**
@@ -310,6 +316,17 @@
     return this._status.OK();
   }
 
+  /**
+   * @param {!Object} message
+   */
+  _onCompleteTraceSession(message) {
+    var session = this._traceSessions.get(message.id);
+    if (!session)
+      return this._status.E_NOTFOUND(message.id);
+    this._traceSessions.delete(message.id);
+    session.complete(message.url, message.timeOffset);
+  }
+
   _onCreateSidebarPane(message) {
     if (message.panel !== 'elements' && message.panel !== 'sources')
       return this._status.E_NOTFOUND(message.panel);
@@ -560,6 +577,7 @@
         port._extensionOrigin, message.id, message.categoryName, message.categoryTooltip);
     this._clientObjects[message.id] = provider;
     this._traceProviders.push(provider);
+    this.dispatchEventToListeners(Extensions.ExtensionServer.Events.TraceProviderAdded);
   }
 
   /**
@@ -964,7 +982,8 @@
 /** @enum {symbol} */
 Extensions.ExtensionServer.Events = {
   SidebarPaneAdded: Symbol('SidebarPaneAdded'),
-  AuditCategoryAdded: Symbol('AuditCategoryAdded')
+  AuditCategoryAdded: Symbol('AuditCategoryAdded'),
+  TraceProviderAdded: Symbol('TraceProviderAdded')
 };
 
 /**
diff --git a/third_party/WebKit/Source/devtools/front_end/extensions/ExtensionTraceProvider.js b/third_party/WebKit/Source/devtools/front_end/extensions/ExtensionTraceProvider.js
index b480c15..6a93dac 100644
--- a/third_party/WebKit/Source/devtools/front_end/extensions/ExtensionTraceProvider.js
+++ b/third_party/WebKit/Source/devtools/front_end/extensions/ExtensionTraceProvider.js
@@ -1,3 +1,7 @@
+// 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.
+
 /**
  * @unrestricted
  */
@@ -14,11 +18,52 @@
     this._categoryName = categoryName;
     this._categoryTooltip = categoryTooltip;
   }
-  start() {
-    Extensions.extensionServer.startTraceRecording(this._id);
+
+  /**
+   * @param {!Extensions.TracingSession} session
+   */
+  start(session) {
+    var sessionId = String(++Extensions.ExtensionTraceProvider._lastSessionId);
+    Extensions.extensionServer.startTraceRecording(this._id, sessionId, session);
   }
 
   stop() {
     Extensions.extensionServer.stopTraceRecording(this._id);
   }
+
+  /**
+   * @return {string}
+   */
+  shortDisplayName() {
+    return this._categoryName;
+  }
+
+  /**
+   * @return {string}
+   */
+  longDisplayName() {
+    return this._categoryTooltip;
+  }
+
+  /**
+   * @return {string}
+   */
+  persistentIdentifier() {
+    return `${this._extensionOrigin}/${this._categoryName}`;
+  }
+};
+
+Extensions.ExtensionTraceProvider._lastSessionId = 0;
+
+/**
+ * @interface
+ */
+Extensions.TracingSession = function() {};
+
+Extensions.TracingSession.prototype = {
+  /**
+   * @param {string} url
+   * @param {number} timeOffsetMicroseconds
+   */
+  complete: function(url, timeOffsetMicroseconds) {}
 };
diff --git a/third_party/WebKit/Source/devtools/front_end/layer_viewer/Layers3DView.js b/third_party/WebKit/Source/devtools/front_end/layer_viewer/Layers3DView.js
index 3acc1f6..d858aff 100644
--- a/third_party/WebKit/Source/devtools/front_end/layer_viewer/Layers3DView.js
+++ b/third_party/WebKit/Source/devtools/front_end/layer_viewer/Layers3DView.js
@@ -231,7 +231,6 @@
   _updateTransformAndConstraints() {
     var paddingFraction = 0.1;
     var viewport = this._layerTree.viewportSize();
-    var root = this._layerTree.root();
     var baseWidth = viewport ? viewport.width : this._dimensionsForAutoscale.width;
     var baseHeight = viewport ? viewport.height : this._dimensionsForAutoscale.height;
     var canvasWidth = this._canvasElement.width;
diff --git a/third_party/WebKit/Source/devtools/front_end/main/Main.js b/third_party/WebKit/Source/devtools/front_end/main/Main.js
index f52322b..3c7ae727 100644
--- a/third_party/WebKit/Source/devtools/front_end/main/Main.js
+++ b/third_party/WebKit/Source/devtools/front_end/main/Main.js
@@ -182,8 +182,7 @@
         new Bindings.BreakpointManager(null, Workspace.workspace, SDK.targetManager, Bindings.debuggerWorkspaceBinding);
     Extensions.extensionServer = new Extensions.ExtensionServer();
 
-    var fileSystemWorkspaceBinding =
-        new Persistence.FileSystemWorkspaceBinding(Workspace.isolatedFileSystemManager, Workspace.workspace);
+    new Persistence.FileSystemWorkspaceBinding(Workspace.isolatedFileSystemManager, Workspace.workspace);
     Persistence.persistence =
         new Persistence.Persistence(Workspace.workspace, Bindings.breakpointManager, Workspace.fileSystemMapping);
 
diff --git a/third_party/WebKit/Source/devtools/front_end/network/NetworkLogView.js b/third_party/WebKit/Source/devtools/front_end/network/NetworkLogView.js
index 8ccc296..45e5f03d 100644
--- a/third_party/WebKit/Source/devtools/front_end/network/NetworkLogView.js
+++ b/third_party/WebKit/Source/devtools/front_end/network/NetworkLogView.js
@@ -796,7 +796,6 @@
 
     this.removeAllNodeHighlights();
 
-    var oldBoundary = this.calculator().boundary();
     this._timeCalculator.updateBoundariesForEventTime(this._mainRequestLoadTime);
     this._durationCalculator.updateBoundariesForEventTime(this._mainRequestLoadTime);
     this._timeCalculator.updateBoundariesForEventTime(this._mainRequestDOMContentLoadedTime);
diff --git a/third_party/WebKit/Source/devtools/front_end/network/NetworkLogViewColumns.js b/third_party/WebKit/Source/devtools/front_end/network/NetworkLogViewColumns.js
index 6fb1f137..7e502ba 100644
--- a/third_party/WebKit/Source/devtools/front_end/network/NetworkLogViewColumns.js
+++ b/third_party/WebKit/Source/devtools/front_end/network/NetworkLogViewColumns.js
@@ -470,7 +470,7 @@
     var index = this._columns.findIndex(columnConfig => columnConfig.id === headerId);
     if (index === -1)
       return false;
-    var columnConfig = this._columns.splice(index, 1);
+    this._columns.splice(index, 1);
     this._dataGrid.removeColumn(headerId);
     this._saveColumns();
     this._updateColumns();
diff --git a/third_party/WebKit/Source/devtools/front_end/network/NetworkOverview.js b/third_party/WebKit/Source/devtools/front_end/network/NetworkOverview.js
index 5b53515..a283e507 100644
--- a/third_party/WebKit/Source/devtools/front_end/network/NetworkOverview.js
+++ b/third_party/WebKit/Source/devtools/front_end/network/NetworkOverview.js
@@ -11,12 +11,6 @@
 
     /** @type {number} */
     this._numBands = 1;
-    /** @type {number} */
-    this._windowStart = 0;
-    /** @type {number} */
-    this._windowEnd = 0;
-    /** @type {boolean} */
-    this._restoringWindow = false;
     /** @type {boolean} */
     this._updateScheduled = false;
 
@@ -118,8 +112,6 @@
    * @override
    */
   reset() {
-    this._windowStart = 0;
-    this._windowEnd = 0;
     /** @type {?Components.FilmStripModel} */
     this._filmStripModel = null;
 
@@ -167,16 +159,9 @@
       var span = calculator.boundarySpan();
       while (this._span < span)
         this._span *= 1.25;
+
       calculator.setBounds(calculator.minimumBoundary(), calculator.minimumBoundary() + this._span);
       this._lastBoundary = new Network.NetworkTimeBoundary(calculator.minimumBoundary(), calculator.maximumBoundary());
-      if (this._windowStart || this._windowEnd) {
-        this._restoringWindow = true;
-        var startTime = calculator.minimumBoundary();
-        var totalTime = calculator.boundarySpan();
-        var left = (this._windowStart - startTime) / totalTime;
-        var right = (this._windowEnd - startTime) / totalTime;
-        this._restoringWindow = false;
-      }
     }
 
     var context = this.context();
diff --git a/third_party/WebKit/Source/devtools/front_end/persistence/Persistence.js b/third_party/WebKit/Source/devtools/front_end/persistence/Persistence.js
index d22a345..8ecf83d 100644
--- a/third_party/WebKit/Source/devtools/front_end/persistence/Persistence.js
+++ b/third_party/WebKit/Source/devtools/front_end/persistence/Persistence.js
@@ -135,6 +135,14 @@
     if (!binding || binding[Persistence.Persistence._muteWorkingCopy])
       return;
     var other = binding.network === uiSourceCode ? binding.fileSystem : binding.network;
+    if (!uiSourceCode.isDirty()) {
+      binding[Persistence.Persistence._muteWorkingCopy] = true;
+      other.resetWorkingCopy();
+      binding[Persistence.Persistence._muteWorkingCopy] = false;
+      this._contentSyncedForTest();
+      return;
+    }
+
     var target = Bindings.NetworkProject.targetForUISourceCode(binding.network);
     if (target.isNodeJS()) {
       var newContent = uiSourceCode.workingCopy();
diff --git a/third_party/WebKit/Source/devtools/front_end/profiler/CPUProfileView.js b/third_party/WebKit/Source/devtools/front_end/profiler/CPUProfileView.js
index 657eb562..3fb466a 100644
--- a/third_party/WebKit/Source/devtools/front_end/profiler/CPUProfileView.js
+++ b/third_party/WebKit/Source/devtools/front_end/profiler/CPUProfileView.js
@@ -229,7 +229,8 @@
       this.dispatchEventToListeners(Profiler.ProfileType.Events.ProfileComplete, recordedProfile);
     }
 
-    this.profileBeingRecorded().target()
+    this.profileBeingRecorded()
+        .target()
         .cpuProfilerModel.stopRecording()
         .then(didStopProfiling.bind(this))
         .then(SDK.targetManager.resumeAllTargets.bind(SDK.targetManager))
@@ -374,7 +375,6 @@
     var entryTotalTimes = new Float32Array(entries.length);
     var entrySelfTimes = new Float32Array(entries.length);
     var entryStartTimes = new Float64Array(entries.length);
-    var minimumBoundary = this.minimumBoundary();
 
     for (var i = 0; i < entries.length; ++i) {
       var entry = entries[i];
diff --git a/third_party/WebKit/Source/devtools/front_end/profiler/ProfilesPanel.js b/third_party/WebKit/Source/devtools/front_end/profiler/ProfilesPanel.js
index 1f28a061..1a61d12 100644
--- a/third_party/WebKit/Source/devtools/front_end/profiler/ProfilesPanel.js
+++ b/third_party/WebKit/Source/devtools/front_end/profiler/ProfilesPanel.js
@@ -608,8 +608,6 @@
     this.profileViews.removeChildren();
     this._profileViewToolbar.removeToolbarItems();
 
-    this.removeAllListeners();
-
     this._profileViewToolbar.element.classList.remove('hidden');
     this.clearResultsButton.element.classList.remove('hidden');
     this.profilesItemTreeElement.select();
diff --git a/third_party/WebKit/Source/devtools/front_end/sass/SASSSupport.js b/third_party/WebKit/Source/devtools/front_end/sass/SASSSupport.js
index 34f8807..642c10f 100644
--- a/third_party/WebKit/Source/devtools/front_end/sass/SASSSupport.js
+++ b/third_party/WebKit/Source/devtools/front_end/sass/SASSSupport.js
@@ -36,7 +36,6 @@
    */
   function createTextNode(payload) {
     var range = Common.TextRange.fromObject(payload);
-    var value = text.extract(range);
     return new Sass.SASSSupport.TextNode(document, text.extract(range), range);
   }
 
diff --git a/third_party/WebKit/Source/devtools/front_end/sdk/CSSParser.js b/third_party/WebKit/Source/devtools/front_end/sdk/CSSParser.js
deleted file mode 100644
index b003f82..0000000
--- a/third_party/WebKit/Source/devtools/front_end/sdk/CSSParser.js
+++ /dev/null
@@ -1,117 +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.
- */
-
-/**
- * @unrestricted
- */
-SDK.CSSParser = class extends Common.Object {
-  constructor() {
-    super();
-    this._rules = [];
-    this._terminated = false;
-  }
-
-  /**
-   * @param {!SDK.CSSStyleSheetHeader} styleSheetHeader
-   * @param {function(!Array.<!Common.FormatterWorkerPool.CSSRule>)=} callback
-   */
-  fetchAndParse(styleSheetHeader, callback) {
-    this._lock();
-    this._finishedCallback = callback;
-    styleSheetHeader.requestContent().then(this._innerParse.bind(this));
-  }
-
-  /**
-   * @param {string} text
-   * @param {function(!Array.<!Common.FormatterWorkerPool.CSSRule>)=} callback
-   */
-  parse(text, callback) {
-    this._lock();
-    this._finishedCallback = callback;
-    this._innerParse(text);
-  }
-
-  /**
-   * @param {string} text
-   * @return {!Promise<!Array.<!Common.FormatterWorkerPool.CSSRule>>}
-   */
-  parsePromise(text) {
-    return new Promise(promiseConstructor.bind(this));
-
-    /**
-     * @param {function()} succ
-     * @param {function()} fail
-     * @this {SDK.CSSParser}
-     */
-    function promiseConstructor(succ, fail) {
-      this.parse(text, succ);
-    }
-  }
-
-  dispose() {
-    if (this._terminated)
-      return;
-    this._terminated = true;
-    this._runFinishedCallback([]);
-  }
-
-  /**
-   * @return {!Array.<!Common.FormatterWorkerPool.CSSRule>}
-   */
-  rules() {
-    return this._rules;
-  }
-
-  _lock() {
-    console.assert(!this._parsingStyleSheet, 'Received request to parse stylesheet before previous was completed.');
-    this._parsingStyleSheet = true;
-  }
-
-  _unlock() {
-    delete this._parsingStyleSheet;
-  }
-
-  /**
-   * @param {?string} text
-   */
-  _innerParse(text) {
-    this._rules = [];
-    Common.formatterWorkerPool.parseCSS(text || '', this._onRuleChunk.bind(this));
-  }
-
-  /**
-   * @param {boolean} isLastChunk
-   * @param {!Array.<!Common.FormatterWorkerPool.CSSRule>} rules
-   */
-  _onRuleChunk(isLastChunk, rules) {
-    if (this._terminated)
-      return;
-    this._rules = this._rules.concat(rules);
-    if (isLastChunk)
-      this._onFinishedParsing();
-    this.dispatchEventToListeners(SDK.CSSParser.Events.RulesParsed);
-  }
-
-  _onFinishedParsing() {
-    this._unlock();
-    this._runFinishedCallback(this._rules);
-  }
-
-  /**
-   * @param {!Array<!SDK.CSSRule>} rules
-   */
-  _runFinishedCallback(rules) {
-    var callback = this._finishedCallback;
-    delete this._finishedCallback;
-    if (callback)
-      callback.call(null, rules);
-  }
-};
-
-/** @enum {symbol} */
-SDK.CSSParser.Events = {
-  RulesParsed: Symbol('RulesParsed')
-};
diff --git a/third_party/WebKit/Source/devtools/front_end/sdk/DOMModel.js b/third_party/WebKit/Source/devtools/front_end/sdk/DOMModel.js
index d95b8d5..b76ab642 100644
--- a/third_party/WebKit/Source/devtools/front_end/sdk/DOMModel.js
+++ b/third_party/WebKit/Source/devtools/front_end/sdk/DOMModel.js
@@ -1807,16 +1807,6 @@
    * @param {function(?Protocol.Error)=} callback
    */
   undo(callback) {
-    /**
-     * @param {?Protocol.Error} error
-     * @this {SDK.DOMModel}
-     */
-    function mycallback(error) {
-      this.dispatchEventToListeners(SDK.DOMModel.Events.UndoRedoCompleted);
-      callback(error);
-    }
-
-    this.dispatchEventToListeners(SDK.DOMModel.Events.UndoRedoRequested);
     this._agent.undo(callback);
   }
 
@@ -1824,16 +1814,6 @@
    * @param {function(?Protocol.Error)=} callback
    */
   redo(callback) {
-    /**
-     * @param {?Protocol.Error} error
-     * @this {SDK.DOMModel}
-     */
-    function mycallback(error) {
-      this.dispatchEventToListeners(SDK.DOMModel.Events.UndoRedoCompleted);
-      callback(error);
-    }
-
-    this.dispatchEventToListeners(SDK.DOMModel.Events.UndoRedoRequested);
     this._agent.redo(callback);
   }
 
@@ -1941,10 +1921,7 @@
   NodeRemoved: Symbol('NodeRemoved'),
   DocumentUpdated: Symbol('DocumentUpdated'),
   ChildNodeCountUpdated: Symbol('ChildNodeCountUpdated'),
-  UndoRedoRequested: Symbol('UndoRedoRequested'),
-  UndoRedoCompleted: Symbol('UndoRedoCompleted'),
   DistributedNodesChanged: Symbol('DistributedNodesChanged'),
-  ModelSuspended: Symbol('ModelSuspended'),
   InspectModeWillBeToggled: Symbol('InspectModeWillBeToggled'),
   MarkersChanged: Symbol('MarkersChanged')
 };
diff --git a/third_party/WebKit/Source/devtools/front_end/sdk/DebuggerModel.js b/third_party/WebKit/Source/devtools/front_end/sdk/DebuggerModel.js
index 177643a..e5802ee 100644
--- a/third_party/WebKit/Source/devtools/front_end/sdk/DebuggerModel.js
+++ b/third_party/WebKit/Source/devtools/front_end/sdk/DebuggerModel.js
@@ -216,7 +216,6 @@
     }
     columnNumber = Math.max(columnNumber, minColumnNumber);
 
-    var target = this.target();
     /**
      * @param {?Protocol.Error} error
      * @param {!Protocol.Debugger.BreakpointId} breakpointId
@@ -240,8 +239,6 @@
    * @param {function(?Protocol.Debugger.BreakpointId, !Array.<!SDK.DebuggerModel.Location>)=} callback
    */
   setBreakpointBySourceId(rawLocation, condition, callback) {
-    var target = this.target();
-
     /**
      * @this {SDK.DebuggerModel}
      * @param {?Protocol.Error} error
diff --git a/third_party/WebKit/Source/devtools/front_end/sdk/SubTargetsManager.js b/third_party/WebKit/Source/devtools/front_end/sdk/SubTargetsManager.js
index 8561c06..ee930ca 100644
--- a/third_party/WebKit/Source/devtools/front_end/sdk/SubTargetsManager.js
+++ b/third_party/WebKit/Source/devtools/front_end/sdk/SubTargetsManager.js
@@ -185,7 +185,6 @@
    * @param {string} targetId
    */
   _detachedFromTarget(targetId) {
-    var target = this._attachedTargets.get(targetId);
     this._attachedTargets.delete(targetId);
     var connection = this._connections.get(targetId);
     connection._onDisconnect.call(null, 'target terminated');
diff --git a/third_party/WebKit/Source/devtools/front_end/sdk/TracingModel.js b/third_party/WebKit/Source/devtools/front_end/sdk/TracingModel.js
index 5e77ce79..996fc50 100644
--- a/third_party/WebKit/Source/devtools/front_end/sdk/TracingModel.js
+++ b/third_party/WebKit/Source/devtools/front_end/sdk/TracingModel.js
@@ -169,6 +169,28 @@
   }
 
   /**
+   * @param {number} offset
+   */
+  adjustTime(offset) {
+    this._minimumRecordTime += offset;
+    this._maximumRecordTime += offset;
+    for (const process of this._processById.values()) {
+      for (const thread of process._threads.values()) {
+        for (const event of thread.events()) {
+          event.startTime += offset;
+          if (typeof event.endTime === 'number')
+            event.endTime += offset;
+        }
+        for (const event of thread.asyncEvents()) {
+          event.startTime += offset;
+          if (typeof event.endTime === 'number')
+            event.endTime += offset;
+        }
+      }
+    }
+  }
+
+  /**
    * @param {!SDK.TracingManager.EventPayload} payload
    */
   _addEvent(payload) {
@@ -474,7 +496,7 @@
 
   finishWriting() {},
 
-  reset() {},
+  reset() {}
 };
 
 /**
@@ -612,10 +634,6 @@
   }
 };
 
-
-/**
- * @unrestricted
- */
 SDK.TracingModel.ObjectSnapshot = class extends SDK.TracingModel.Event {
   /**
    * @param {string} category
@@ -625,6 +643,12 @@
    */
   constructor(category, name, startTime, thread) {
     super(category, name, SDK.TracingModel.Phase.SnapshotObject, startTime, thread);
+    /** @type {?function():!Promise<?string>} */
+    this._backingStorage = null;
+    /** @type {string} */
+    this.id;
+    /** @type {?Promise<?>} */
+    this._objectPromise = null;
   }
 
   /**
@@ -743,11 +767,19 @@
   }
 };
 
-/**
- * @unrestricted
- */
 SDK.TracingModel.NamedObject = class {
   /**
+   * @param {!SDK.TracingModel} model
+   * @param {number} id
+   */
+  constructor(model, id) {
+    this._model = model;
+    this._id = id;
+    this._name = '';
+    this._sortIndex = 0;
+  }
+
+  /**
    * @param {!Array.<!SDK.TracingModel.NamedObject>} array
    */
   static _sort(array) {
@@ -783,23 +815,16 @@
   }
 };
 
-
-/**
- * @unrestricted
- */
 SDK.TracingModel.Process = class extends SDK.TracingModel.NamedObject {
   /**
    * @param {!SDK.TracingModel} model
    * @param {number} id
    */
   constructor(model, id) {
-    super();
-    this._setName('Process ' + id);
-    this._id = id;
+    super(model, id);
     /** @type {!Map<number, !SDK.TracingModel.Thread>} */
     this._threads = new Map();
     this._threadByName = new Map();
-    this._model = model;
   }
 
   /**
@@ -854,22 +879,17 @@
   }
 };
 
-/**
- * @unrestricted
- */
 SDK.TracingModel.Thread = class extends SDK.TracingModel.NamedObject {
   /**
    * @param {!SDK.TracingModel.Process} process
    * @param {number} id
    */
   constructor(process, id) {
-    super();
+    super(process._model, id);
     this._process = process;
-    this._setName('Thread ' + id);
     this._events = [];
     this._asyncEvents = [];
-    this._id = id;
-    this._model = process._model;
+    this._lastTopLevelEvent = null;
   }
 
   tracingComplete() {
diff --git a/third_party/WebKit/Source/devtools/front_end/sdk/module.json b/third_party/WebKit/Source/devtools/front_end/sdk/module.json
index 339aa901..4094fb4 100644
--- a/third_party/WebKit/Source/devtools/front_end/sdk/module.json
+++ b/third_party/WebKit/Source/devtools/front_end/sdk/module.json
@@ -88,7 +88,6 @@
         "CSSMedia.js",
         "CSSMetadata.js",
         "CSSModel.js",
-        "CSSParser.js",
         "CSSProperty.js",
         "CSSRule.js",
         "CSSStyleDeclaration.js",
diff --git a/third_party/WebKit/Source/devtools/front_end/source_frame/SourcesTextEditor.js b/third_party/WebKit/Source/devtools/front_end/source_frame/SourcesTextEditor.js
index 749a87f..e8578d71 100644
--- a/third_party/WebKit/Source/devtools/front_end/source_frame/SourcesTextEditor.js
+++ b/third_party/WebKit/Source/devtools/front_end/source_frame/SourcesTextEditor.js
@@ -541,7 +541,6 @@
 
     function modeConstructor(config, parserConfig) {
       function nextToken(stream) {
-        var pos = stream.pos;
         if (stream.match(/^\s+$/, true))
           return true ? 'trailing-whitespace' : null;
         do
diff --git a/third_party/WebKit/Source/devtools/front_end/sources/FilteredUISourceCodeListDelegate.js b/third_party/WebKit/Source/devtools/front_end/sources/FilteredUISourceCodeListDelegate.js
index 3711cf5..b15e0cd7 100644
--- a/third_party/WebKit/Source/devtools/front_end/sources/FilteredUISourceCodeListDelegate.js
+++ b/third_party/WebKit/Source/devtools/front_end/sources/FilteredUISourceCodeListDelegate.js
@@ -123,7 +123,7 @@
     var uiSourceCode = this._uiSourceCodes[itemIndex];
     var fullDisplayName = uiSourceCode.fullDisplayName();
     var indexes = [];
-    var score = new Sources.FilePathScoreFunction(query).score(fullDisplayName, indexes);
+    new Sources.FilePathScoreFunction(query).score(fullDisplayName, indexes);
     var fileNameIndex = fullDisplayName.lastIndexOf('/');
 
     titleElement.textContent = uiSourceCode.displayName() + (this._queryLineNumberAndColumnNumber || '');
diff --git a/third_party/WebKit/Source/devtools/front_end/sources/JavaScriptSourceFrame.js b/third_party/WebKit/Source/devtools/front_end/sources/JavaScriptSourceFrame.js
index d6ecba64..cadf734 100644
--- a/third_party/WebKit/Source/devtools/front_end/sources/JavaScriptSourceFrame.js
+++ b/third_party/WebKit/Source/devtools/front_end/sources/JavaScriptSourceFrame.js
@@ -417,7 +417,6 @@
   }
 
   _resolveObjectForPopover(anchorBox, showCallback, objectGroupName) {
-    var target = UI.context.flavor(SDK.Target);
     var selectedCallFrame = UI.context.flavor(SDK.DebuggerModel.CallFrame);
     if (!selectedCallFrame) {
       this._popoverHelper.hidePopover();
diff --git a/third_party/WebKit/Source/devtools/front_end/sources/NavigatorView.js b/third_party/WebKit/Source/devtools/front_end/sources/NavigatorView.js
index e11f4e8..d8fe76b 100644
--- a/third_party/WebKit/Source/devtools/front_end/sources/NavigatorView.js
+++ b/third_party/WebKit/Source/devtools/front_end/sources/NavigatorView.js
@@ -157,7 +157,6 @@
     var typeWeight1 = Sources.NavigatorView._treeElementOrder(treeElement1);
     var typeWeight2 = Sources.NavigatorView._treeElementOrder(treeElement2);
 
-    var result;
     if (typeWeight1 > typeWeight2)
       return 1;
     if (typeWeight1 < typeWeight2)
@@ -705,9 +704,6 @@
    * @param {!Workspace.UISourceCode=} uiSourceCodeToCopy
    */
   create(project, path, uiSourceCodeToCopy) {
-    var filePath;
-    var uiSourceCode;
-
     /**
      * @this {Sources.NavigatorView}
      * @param {?string} content
diff --git a/third_party/WebKit/Source/devtools/front_end/sources/SourceMapNamesResolver.js b/third_party/WebKit/Source/devtools/front_end/sources/SourceMapNamesResolver.js
index 11d80c2e..8768ac0 100644
--- a/third_party/WebKit/Source/devtools/front_end/sources/SourceMapNamesResolver.js
+++ b/third_party/WebKit/Source/devtools/front_end/sources/SourceMapNamesResolver.js
@@ -65,7 +65,6 @@
   function onIdentifiers(text, scopeStart, prefix, identifiers) {
     var result = [];
     var cursor = new Common.TextCursor(text.lineEndings());
-    var promises = [];
     for (var i = 0; i < identifiers.length; ++i) {
       var id = identifiers[i];
       if (id.offset < prefix.length)
diff --git a/third_party/WebKit/Source/devtools/front_end/sources/SourcesPanel.js b/third_party/WebKit/Source/devtools/front_end/sources/SourcesPanel.js
index b373530..97e6550 100644
--- a/third_party/WebKit/Source/devtools/front_end/sources/SourcesPanel.js
+++ b/third_party/WebKit/Source/devtools/front_end/sources/SourcesPanel.js
@@ -511,7 +511,6 @@
     delete this._switchToPausedTargetTimeout;
     if (this._paused)
       return;
-    var target = UI.context.flavor(SDK.Target);
     if (debuggerModel.isPaused())
       return;
     var debuggerModels = SDK.DebuggerModel.instances();
@@ -855,7 +854,6 @@
       return;
     var uiLocation = /** @type {!Workspace.UILocation} */ (object);
     var uiSourceCode = uiLocation.uiSourceCode;
-    var projectType = uiSourceCode.project().type();
 
     var contentType = uiSourceCode.contentType();
     if (contentType.hasScripts()) {
diff --git a/third_party/WebKit/Source/devtools/front_end/sources/StyleSheetOutlineDialog.js b/third_party/WebKit/Source/devtools/front_end/sources/StyleSheetOutlineDialog.js
index 83dc4ba..c8b9802d 100644
--- a/third_party/WebKit/Source/devtools/front_end/sources/StyleSheetOutlineDialog.js
+++ b/third_party/WebKit/Source/devtools/front_end/sources/StyleSheetOutlineDialog.js
@@ -37,9 +37,12 @@
   constructor(uiSourceCode, selectItemCallback) {
     super([]);
     this._selectItemCallback = selectItemCallback;
-    this._cssParser = new SDK.CSSParser();
-    this._cssParser.addEventListener(SDK.CSSParser.Events.RulesParsed, this.refresh.bind(this));
-    this._cssParser.parse(uiSourceCode.workingCopy());
+    /** @type {!Array<!Common.FormatterWorkerPool.CSSRule>} */
+    this._rules = [];
+    Common.formatterWorkerPool.parseCSS(uiSourceCode.workingCopy(), (isLastChunk, rules) => {
+      this._rules.push(...rules);
+      this.refresh();
+    });
   }
 
   /**
@@ -57,7 +60,7 @@
    * @return {number}
    */
   itemCount() {
-    return this._cssParser.rules().length;
+    return this._rules.length;
   }
 
   /**
@@ -66,7 +69,7 @@
    * @return {string}
    */
   itemKeyAt(itemIndex) {
-    var rule = this._cssParser.rules()[itemIndex];
+    var rule = this._rules[itemIndex];
     return rule.selectorText || rule.atRule;
   }
 
@@ -77,7 +80,7 @@
    * @return {number}
    */
   itemScoreAt(itemIndex, query) {
-    var rule = this._cssParser.rules()[itemIndex];
+    var rule = this._rules[itemIndex];
     return -rule.lineNumber;
   }
 
@@ -89,7 +92,7 @@
    * @param {!Element} subtitleElement
    */
   renderItem(itemIndex, query, titleElement, subtitleElement) {
-    var rule = this._cssParser.rules()[itemIndex];
+    var rule = this._rules[itemIndex];
     titleElement.textContent = rule.selectorText || rule.atRule;
     this.highlightRanges(titleElement, query);
     subtitleElement.textContent = ':' + (rule.lineNumber + 1);
@@ -101,16 +104,9 @@
    * @param {string} promptValue
    */
   selectItem(itemIndex, promptValue) {
-    var rule = this._cssParser.rules()[itemIndex];
+    var rule = this._rules[itemIndex];
     var lineNumber = rule.lineNumber;
     if (!isNaN(lineNumber) && lineNumber >= 0)
       this._selectItemCallback(lineNumber, rule.columnNumber);
   }
-
-  /**
-   * @override
-   */
-  dispose() {
-    this._cssParser.dispose();
-  }
 };
diff --git a/third_party/WebKit/Source/devtools/front_end/sources/TabbedEditorContainer.js b/third_party/WebKit/Source/devtools/front_end/sources/TabbedEditorContainer.js
index 8e163c3..ed3add0 100644
--- a/third_party/WebKit/Source/devtools/front_end/sources/TabbedEditorContainer.js
+++ b/third_party/WebKit/Source/devtools/front_end/sources/TabbedEditorContainer.js
@@ -298,7 +298,6 @@
     if (!shouldPrompt ||
         confirm(Common.UIString('Are you sure you want to close unsaved file: %s?', uiSourceCode.name()))) {
       uiSourceCode.resetWorkingCopy();
-      var previousView = this._currentView;
       if (nextTabId)
         this._tabbedPane.selectTab(nextTabId, true);
       this._tabbedPane.closeTab(id, true);
diff --git a/third_party/WebKit/Source/devtools/front_end/timeline/CountersGraph.js b/third_party/WebKit/Source/devtools/front_end/timeline/CountersGraph.js
index b48a7cf..694eb94 100644
--- a/third_party/WebKit/Source/devtools/front_end/timeline/CountersGraph.js
+++ b/third_party/WebKit/Source/devtools/front_end/timeline/CountersGraph.js
@@ -232,6 +232,12 @@
   refreshRecords() {
   }
 
+  /**
+   * @override
+   */
+  extensionDataAdded() {
+  }
+
   _clear() {
     var ctx = this._canvas.getContext('2d');
     ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
diff --git a/third_party/WebKit/Source/devtools/front_end/timeline/ExtensionTracingSession.js b/third_party/WebKit/Source/devtools/front_end/timeline/ExtensionTracingSession.js
new file mode 100644
index 0000000..de108a8
--- /dev/null
+++ b/third_party/WebKit/Source/devtools/front_end/timeline/ExtensionTracingSession.js
@@ -0,0 +1,78 @@
+// 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.
+
+/**
+ * @implements {Extensions.TracingSession}
+ * @implements {Timeline.LoaderClient}
+ */
+Timeline.ExtensionTracingSession = class {
+  /**
+   * @param {!Extensions.ExtensionTraceProvider} provider
+   * @param {!Timeline.TimelineLifecycleDelegate} delegate
+   */
+  constructor(provider, delegate) {
+    this._provider = provider;
+    this._delegate = delegate;
+    this._sessionGeneration = delegate.sessionGeneration();
+    /** @type {function()} */
+    this._completionCallback;
+    this._completionPromise = new Promise(fulfill => {this._completionCallback = fulfill;});
+    /** @type {?SDK.TracingModel} */
+    this._tracingModel = null;
+    /** @type {number} */
+    this._timeOffset = 0;
+  }
+
+  /** @override */
+  loadingStarted() {
+  }
+
+  /**
+   * @override
+   * @param {number=} progress
+   */
+  loadingProgress(progress) {
+  }
+
+  /**
+   * @override
+   * @param {boolean} success
+   */
+  loadingComplete(success) {
+    if (!success || this._sessionGeneration !== this._delegate.sessionGeneration()) {
+      this._tracingModel.reset();
+    } else {
+      this._delegate.addExtensionEvents(
+          this._provider.longDisplayName(),
+          /** @type {!SDK.TracingModel} */ (this._tracingModel), this._timeOffset);
+    }
+    this._completionCallback();
+  }
+
+  /**
+   * @override
+   * @param {string} url
+   * @param {number} timeOffsetMicroseconds
+   */
+  complete(url, timeOffsetMicroseconds) {
+    if (!url || this._sessionGeneration !== this._delegate.sessionGeneration()) {
+      this._completionCallback();
+      return;
+    }
+    var storage = new Bindings.TempFileBackingStorage('tracing');
+    this._tracingModel = new SDK.TracingModel(storage);
+    this._timeOffset = timeOffsetMicroseconds;
+    Timeline.TimelineLoader.loadFromURL(this._tracingModel, url, this);
+  }
+
+  start() {
+    this._provider.start(this);
+  }
+
+  /** @return {!Promise<string>} */
+  stop() {
+    this._provider.stop();
+    return this._completionPromise;
+  }
+};
diff --git a/third_party/WebKit/Source/devtools/front_end/timeline/MemoryCountersGraph.js b/third_party/WebKit/Source/devtools/front_end/timeline/MemoryCountersGraph.js
index 99878329..ec8a943 100644
--- a/third_party/WebKit/Source/devtools/front_end/timeline/MemoryCountersGraph.js
+++ b/third_party/WebKit/Source/devtools/front_end/timeline/MemoryCountersGraph.js
@@ -80,4 +80,10 @@
     }
     this.scheduleRefresh();
   }
+
+  /**
+   * @override
+   */
+  extensionDataAdded() {
+  }
 };
diff --git a/third_party/WebKit/Source/devtools/front_end/timeline/TimelineController.js b/third_party/WebKit/Source/devtools/front_end/timeline/TimelineController.js
index c646739..d189476 100644
--- a/third_party/WebKit/Source/devtools/front_end/timeline/TimelineController.js
+++ b/third_party/WebKit/Source/devtools/front_end/timeline/TimelineController.js
@@ -2,9 +2,6 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-/** @typedef {!{range: !Protocol.CSS.SourceRange, styleSheetId: !Protocol.CSS.StyleSheetId, wasUsed: boolean}} */
-SDK.CSSModel.RuleUsage;
-
 /**
  * @implements {SDK.TargetManager.Observer}
  * @implements {SDK.TracingManagerClient}
@@ -21,6 +18,8 @@
     this._target = target;
     this._tracingModel = tracingModel;
     this._targets = [];
+    /** @type {!Array<!Timeline.ExtensionTracingSession>} */
+    this._extensionSessions = [];
     SDK.targetManager.observeTargets(this);
 
     if (Runtime.experiments.isEnabled('timelineRuleUsageRecording'))
@@ -28,13 +27,10 @@
   }
 
   /**
-   * @param {boolean} captureCauses
-   * @param {boolean} enableJSSampling
-   * @param {boolean} captureMemory
-   * @param {boolean} capturePictures
-   * @param {boolean} captureFilmStrip
+   * @param {!Timeline.TimelineController.CaptureOptions} options
+   * @param {!Array<!Extensions.ExtensionTraceProvider>} providers
    */
-  startRecording(captureCauses, enableJSSampling, captureMemory, capturePictures, captureFilmStrip) {
+  startRecording(options, providers) {
     this._extensionTraceProviders = Extensions.extensionServer.traceProviders().slice();
 
     function disabledByDefault(category) {
@@ -47,30 +43,29 @@
     ];
     categoriesArray.push(TimelineModel.TimelineModel.Category.LatencyInfo);
 
-    if (Runtime.experiments.isEnabled('timelineV8RuntimeCallStats') && enableJSSampling)
+    if (Runtime.experiments.isEnabled('timelineV8RuntimeCallStats') && options.enableJSSampling)
       categoriesArray.push(disabledByDefault('v8.runtime_stats_sampling'));
-    if (Runtime.experiments.isEnabled('timelineTracingJSProfile') && enableJSSampling) {
+    if (Runtime.experiments.isEnabled('timelineTracingJSProfile') && options.enableJSSampling) {
       categoriesArray.push(disabledByDefault('v8.cpu_profiler'));
       if (Common.moduleSetting('highResolutionCpuProfiling').get())
         categoriesArray.push(disabledByDefault('v8.cpu_profiler.hires'));
     }
-    if (captureCauses || enableJSSampling)
+    if (options.captureCauses || options.enableJSSampling)
       categoriesArray.push(disabledByDefault('devtools.timeline.stack'));
-    if (captureCauses && Runtime.experiments.isEnabled('timelineInvalidationTracking'))
+    if (options.captureCauses && Runtime.experiments.isEnabled('timelineInvalidationTracking'))
       categoriesArray.push(disabledByDefault('devtools.timeline.invalidationTracking'));
-    if (capturePictures) {
+    if (options.capturePictures) {
       categoriesArray.push(
           disabledByDefault('devtools.timeline.layers'), disabledByDefault('devtools.timeline.picture'),
           disabledByDefault('blink.graphics_context_annotations'));
     }
-    if (captureFilmStrip)
+    if (options.captureFilmStrip)
       categoriesArray.push(disabledByDefault('devtools.screenshot'));
 
-    for (var traceProvider of this._extensionTraceProviders)
-      traceProvider.start();
-
+    this._extensionSessions = providers.map(provider => new Timeline.ExtensionTracingSession(provider, this._delegate));
+    this._extensionSessions.forEach(session => session.start());
     var categories = categoriesArray.join(',');
-    this._startRecordingWithCategories(categories, enableJSSampling);
+    this._startRecordingWithCategories(categories, options.enableJSSampling);
   }
 
   stopRecording() {
@@ -84,12 +79,16 @@
     else
       this._addUnusedRulesToCoverage();
 
-    Promise.all(tracingStoppedPromises).then(() => this._allSourcesFinished());
-
     this._delegate.loadingStarted();
 
-    for (var traceProvider of this._extensionTraceProviders)
-      traceProvider.stop();
+    var extensionCompletionPromises = this._extensionSessions.map(session => session.stop());
+    if (extensionCompletionPromises.length) {
+      var timerId;
+      var timeoutPromise = new Promise(fulfill => timerId = setTimeout(fulfill, 5000));
+      tracingStoppedPromises.push(
+          Promise.race([Promise.all(extensionCompletionPromises).then(() => clearTimeout(timerId)), timeoutPromise]));
+    }
+    Promise.all(tracingStoppedPromises).then(() => this._allSourcesFinished());
   }
 
   /**
@@ -308,3 +307,13 @@
     this._delegate.loadingProgress(progress);
   }
 };
+
+/** @typedef {!{
+ *    captureCauses: (boolean|undefined),
+ *    enableJSSampling: (boolean|undefined),
+ *    captureMemory: (boolean|undefined),
+ *    capturePictures: (boolean|undefined),
+ *    captureFilmStrip: (boolean|undefined)
+ *  }}
+ */
+Timeline.TimelineController.CaptureOptions;
diff --git a/third_party/WebKit/Source/devtools/front_end/timeline/TimelineFlameChart.js b/third_party/WebKit/Source/devtools/front_end/timeline/TimelineFlameChart.js
index fdbc8a73c..e8e45a1 100644
--- a/third_party/WebKit/Source/devtools/front_end/timeline/TimelineFlameChart.js
+++ b/third_party/WebKit/Source/devtools/front_end/timeline/TimelineFlameChart.js
@@ -47,6 +47,8 @@
     this._irModel = irModel;
     this._consoleColorGenerator =
         new UI.FlameChart.ColorGenerator({min: 30, max: 55}, {min: 70, max: 100, count: 6}, 50, 0.7);
+    this._extensionColorGenerator =
+        new UI.FlameChart.ColorGenerator({min: 210, max: 300}, {min: 70, max: 100, count: 6}, 70, 0.7);
     const font = this.font();
 
     this._headerLevel1 = {
@@ -102,7 +104,7 @@
   entryTitle(entryIndex) {
     var entryType = this._entryType(entryIndex);
     if (entryType === Timeline.TimelineFlameChartEntryType.Event) {
-      var event = /** @type {!SDK.TracingModel.Event} */ (this._entryData[entryIndex]);
+      const event = /** @type {!SDK.TracingModel.Event} */ (this._entryData[entryIndex]);
       if (event.phase === SDK.TracingModel.Phase.AsyncStepInto || event.phase === SDK.TracingModel.Phase.AsyncStepPast)
         return event.name + ':' + event.args['step'];
       if (event._blackboxRoot)
@@ -114,6 +116,10 @@
         return detailsText;
       return detailsText ? Common.UIString('%s (%s)', name, detailsText) : name;
     }
+    if (entryType === Timeline.TimelineFlameChartEntryType.ExtensionEvent) {
+      const event = /** @type {!SDK.TracingModel.Event} */ (this._entryData[entryIndex]);
+      return event.name;
+    }
     var title = this._entryIndexToTitle[entryIndex];
     if (!title) {
       title = Common.UIString('Unexpected entryIndex %d', entryIndex);
@@ -153,6 +159,8 @@
     this._asyncColorByCategory = new Map();
     /** @type {!Map<!TimelineModel.TimelineIRModel.Phases, string>} */
     this._asyncColorByInteractionPhase = new Map();
+    /** @type {!Array<!{title: string, model: !SDK.TracingModel}>} */
+    this._extensionInfo = [];
   }
 
   /**
@@ -173,16 +181,18 @@
     this._appendHeader(Common.UIString('Interactions'), this._interactionsHeaderLevel1);
     this._appendInteractionRecords();
 
+    var eventEntryType = Timeline.TimelineFlameChartEntryType.Event;
+
     var asyncEventGroups = TimelineModel.TimelineModel.AsyncEventGroup;
     var inputLatencies = this._model.mainThreadAsyncEvents().get(asyncEventGroups.input);
     if (inputLatencies && inputLatencies.length) {
       var title = Timeline.TimelineUIUtils.titleForAsyncEventGroup(asyncEventGroups.input);
-      this._appendAsyncEventsGroup(title, inputLatencies, this._interactionsHeaderLevel2);
+      this._appendAsyncEventsGroup(title, inputLatencies, this._interactionsHeaderLevel2, eventEntryType);
     }
     var animations = this._model.mainThreadAsyncEvents().get(asyncEventGroups.animation);
     if (animations && animations.length) {
       var title = Timeline.TimelineUIUtils.titleForAsyncEventGroup(asyncEventGroups.animation);
-      this._appendAsyncEventsGroup(title, animations, this._interactionsHeaderLevel2);
+      this._appendAsyncEventsGroup(title, animations, this._interactionsHeaderLevel2, eventEntryType);
     }
     var threads = this._model.virtualThreads();
     if (!Runtime.experiments.isEnabled('timelinePerFrameTrack')) {
@@ -203,13 +213,18 @@
       this._appendHeader(Common.UIString('Raster'), this._headerLevel1);
       for (var i = 0; i < compositorThreads.length; ++i) {
         this._appendSyncEvents(
-            compositorThreads[i].events, Common.UIString('Rasterizer Thread %d', i), this._headerLevel2);
+            compositorThreads[i].events, Common.UIString('Rasterizer Thread %d', i), this._headerLevel2,
+            eventEntryType);
       }
     }
     this._appendGPUEvents();
 
     otherThreads.forEach(
-        thread => this._appendThreadTimelineData(thread.name, thread.events, thread.asyncEventsByGroup));
+        thread => this._appendThreadTimelineData(
+            thread.name || Common.UIString('Thread %d', thread.id), thread.events, thread.asyncEventsByGroup));
+
+    for (let extensionIndex = 0; extensionIndex < this._extensionInfo.length; extensionIndex++)
+      this._innerAppendExtensionEvents(extensionIndex);
 
     /**
      * @param {!Timeline.TimelineFlameChartMarker} a
@@ -235,7 +250,7 @@
     clonedHeader.nestingLevel = level;
     this._appendSyncEvents(
         events, Timeline.TimelineUIUtils.displayNameForFrame(frame),
-        /** @type {!UI.FlameChart.GroupStyle} */ (clonedHeader));
+        /** @type {!UI.FlameChart.GroupStyle} */ (clonedHeader), Timeline.TimelineFlameChartEntryType.Event);
     frame.children.forEach(this._appendFrameEvents.bind(this, level + 1));
   }
 
@@ -246,23 +261,26 @@
    * @param {boolean=} forceExpanded
    */
   _appendThreadTimelineData(threadTitle, syncEvents, asyncEvents, forceExpanded) {
+    var entryType = Timeline.TimelineFlameChartEntryType.Event;
     this._appendAsyncEvents(asyncEvents);
-    this._appendSyncEvents(syncEvents, threadTitle, this._headerLevel1, forceExpanded);
+    this._appendSyncEvents(syncEvents, threadTitle, this._headerLevel1, entryType, forceExpanded);
   }
 
   /**
    * @param {!Array<!SDK.TracingModel.Event>} events
    * @param {string} title
    * @param {!UI.FlameChart.GroupStyle} style
+   * @param {!Timeline.TimelineFlameChartEntryType} entryType
    * @param {boolean=} forceExpanded
    */
-  _appendSyncEvents(events, title, style, forceExpanded) {
+  _appendSyncEvents(events, title, style, entryType, forceExpanded) {
+    var isExtension = entryType === Timeline.TimelineFlameChartEntryType.ExtensionEvent;
     var openEvents = [];
-    var blackboxingEnabled = Runtime.experiments.isEnabled('blackboxJSFramesOnTimeline');
+    var blackboxingEnabled = !isExtension && Runtime.experiments.isEnabled('blackboxJSFramesOnTimeline');
     var maxStackDepth = 0;
     for (var i = 0; i < events.length; ++i) {
       var e = events[i];
-      if (TimelineModel.TimelineModel.isMarkerEvent(e)) {
+      if (!isExtension && TimelineModel.TimelineModel.isMarkerEvent(e)) {
         this._markers.push(new Timeline.TimelineFlameChartMarker(
             e.startTime, e.startTime - this._model.minimumRecordTime(),
             Timeline.TimelineUIUtils.markerStyleForEvent(e)));
@@ -272,7 +290,7 @@
           continue;
         if (SDK.TracingModel.isAsyncPhase(e.phase))
           continue;
-        if (!this._isVisible(e))
+        if (!isExtension && !this._isVisible(e))
           continue;
       }
       while (openEvents.length && openEvents.peekLast().endTime <= e.startTime)
@@ -291,12 +309,15 @@
 
       var level = this._currentLevel + openEvents.length;
       this._appendEvent(e, level);
+      if (!isExtension && TimelineModel.TimelineModel.isMarkerEvent(e))
+        this._timelineData.entryTotalTimes[this._entryData.length] = undefined;
+
       maxStackDepth = Math.max(maxStackDepth, openEvents.length + 1);
       if (e.endTime)
         openEvents.push(e);
     }
     this._entryTypeByLevel.length = this._currentLevel + maxStackDepth;
-    this._entryTypeByLevel.fill(Timeline.TimelineFlameChartEntryType.Event, this._currentLevel);
+    this._entryTypeByLevel.fill(entryType, this._currentLevel);
     this._currentLevel += maxStackDepth;
   }
 
@@ -323,6 +344,7 @@
    * @param {!Map<!TimelineModel.TimelineModel.AsyncEventGroup, !Array<!SDK.TracingModel.AsyncEvent>>} asyncEvents
    */
   _appendAsyncEvents(asyncEvents) {
+    var entryType = Timeline.TimelineFlameChartEntryType.Event;
     var groups = TimelineModel.TimelineModel.AsyncEventGroup;
     var groupArray = Object.keys(groups).map(key => groups[key]);
 
@@ -335,7 +357,7 @@
       if (!events)
         continue;
       var title = Timeline.TimelineUIUtils.titleForAsyncEventGroup(group);
-      this._appendAsyncEventsGroup(title, events, this._headerLevel1);
+      this._appendAsyncEventsGroup(title, events, this._headerLevel1, entryType);
     }
   }
 
@@ -343,8 +365,9 @@
    * @param {string} header
    * @param {!Array<!SDK.TracingModel.AsyncEvent>} events
    * @param {!UI.FlameChart.GroupStyle} style
+   * @param {!Timeline.TimelineFlameChartEntryType} entryType
    */
-  _appendAsyncEventsGroup(header, events, style) {
+  _appendAsyncEventsGroup(header, events, style, entryType) {
     var lastUsedTimeByLevel = [];
     var groupHeaderAppended = false;
     for (var i = 0; i < events.length; ++i) {
@@ -363,12 +386,14 @@
       lastUsedTimeByLevel[level] = asyncEvent.endTime;
     }
     this._entryTypeByLevel.length = this._currentLevel + lastUsedTimeByLevel.length;
-    this._entryTypeByLevel.fill(Timeline.TimelineFlameChartEntryType.Event, this._currentLevel);
+    this._entryTypeByLevel.fill(entryType, this._currentLevel);
     this._currentLevel += lastUsedTimeByLevel.length;
   }
 
   _appendGPUEvents() {
-    if (this._appendSyncEvents(this._model.gpuEvents(), Common.UIString('GPU'), this._headerLevel1, false))
+    const eventType = Timeline.TimelineFlameChartEntryType.Event;
+    const gpuEvents = this._model.gpuEvents();
+    if (this._appendSyncEvents(gpuEvents, Common.UIString('GPU'), this._headerLevel1, eventType, false))
       ++this._currentLevel;
   }
 
@@ -484,6 +509,10 @@
       return 'white';
     if (type === Timeline.TimelineFlameChartEntryType.InteractionRecord)
       return 'transparent';
+    if (type === Timeline.TimelineFlameChartEntryType.ExtensionEvent) {
+      var event = /** @type {!SDK.TracingModel.Event} */ (this._entryData[entryIndex]);
+      return this._extensionColorGenerator.colorForID(event.name);
+    }
     return '';
   }
 
@@ -582,6 +611,32 @@
   }
 
   /**
+   * @param {!{title: string, model: !SDK.TracingModel}} entry
+   */
+  appendExtensionEvents(entry) {
+    this._extensionInfo.push(entry);
+    if (this._timelineData)
+      this._innerAppendExtensionEvents(this._extensionInfo.length - 1);
+  }
+
+  /**
+   * @param {number} index
+   */
+  _innerAppendExtensionEvents(index) {
+    var entry = this._extensionInfo[index];
+    var entryType = Timeline.TimelineFlameChartEntryType.ExtensionEvent;
+    var allThreads = [].concat(...entry.model.sortedProcesses().map(process => process.sortedThreads()));
+    if (!allThreads.length)
+      return;
+
+    this._appendHeader(entry.title, this._headerLevel1);
+    for (let thread of allThreads) {
+      this._appendAsyncEventsGroup(thread.name(), thread.asyncEvents(), this._headerLevel2, entryType);
+      this._appendSyncEvents(thread.events(), thread.name(), this._headerLevel2, entryType, false);
+    }
+  }
+
+  /**
    * @param {string} title
    * @param {!UI.FlameChart.GroupStyle} style
    * @param {boolean=} expanded
@@ -598,12 +653,8 @@
     var index = this._entryData.length;
     this._entryData.push(event);
     this._timelineData.entryLevels[index] = level;
-    var duration;
-    if (TimelineModel.TimelineModel.isMarkerEvent(event))
-      duration = undefined;
-    else
-      duration = event.duration || Timeline.TimelineFlameChartDataProvider.InstantEventVisibleDurationMs;
-    this._timelineData.entryTotalTimes[index] = duration;
+    this._timelineData.entryTotalTimes[index] =
+        event.duration || Timeline.TimelineFlameChartDataProvider.InstantEventVisibleDurationMs;
     this._timelineData.entryStartTimes[index] = event.startTime;
   }
 
diff --git a/third_party/WebKit/Source/devtools/front_end/timeline/TimelineFlameChartView.js b/third_party/WebKit/Source/devtools/front_end/timeline/TimelineFlameChartView.js
index 8e10f9a..7dbc9ce8 100644
--- a/third_party/WebKit/Source/devtools/front_end/timeline/TimelineFlameChartView.js
+++ b/third_party/WebKit/Source/devtools/front_end/timeline/TimelineFlameChartView.js
@@ -192,6 +192,7 @@
   Frame: Symbol('Frame'),
   Event: Symbol('Event'),
   InteractionRecord: Symbol('InteractionRecord'),
+  ExtensionEvent: Symbol('ExtensionEvent')
 };
 
 /**
@@ -283,14 +284,15 @@
    * @param {!TimelineModel.TimelineModel} timelineModel
    * @param {!TimelineModel.TimelineFrameModel} frameModel
    * @param {!TimelineModel.TimelineIRModel} irModel
+   * @param {!Array<!{title: string, model: !SDK.TracingModel}>} extensionModels
    * @param {!Array<!TimelineModel.TimelineModel.Filter>} filters
    */
-  constructor(delegate, timelineModel, frameModel, irModel, filters) {
+  constructor(delegate, timelineModel, frameModel, irModel, extensionModels, filters) {
     super();
     this.element.classList.add('timeline-flamechart');
     this._delegate = delegate;
     this._model = timelineModel;
-
+    this._extensionModels = extensionModels;
     this._splitWidget = new UI.SplitWidget(false, false, 'timelineFlamechartMainView', 150);
 
     this._dataProvider = new Timeline.TimelineFlameChartDataProvider(this._model, frameModel, irModel, filters);
@@ -311,6 +313,8 @@
     this._onNetworkEntrySelected = this._onEntrySelected.bind(this, this._networkDataProvider);
     this._mainView.addEventListener(UI.FlameChart.Events.EntrySelected, this._onMainEntrySelected, this);
     this._networkView.addEventListener(UI.FlameChart.Events.EntrySelected, this._onNetworkEntrySelected, this);
+    this._nextExtensionIndex = 0;
+
     Bindings.blackboxManager.addChangeListener(this.refreshRecords, this);
   }
 
@@ -354,13 +358,25 @@
    */
   refreshRecords() {
     this._dataProvider.reset();
+    this._nextExtensionIndex = 0;
+    this.extensionDataAdded();
     this._mainView.scheduleUpdate();
+
     this._networkDataProvider.reset();
     this._networkView.scheduleUpdate();
   }
 
   /**
    * @override
+   */
+  extensionDataAdded() {
+    while (this._nextExtensionIndex < this._extensionModels.length)
+      this._dataProvider.appendExtensionEvents(this._extensionModels[this._nextExtensionIndex++]);
+    this._mainView.scheduleUpdate();
+  }
+
+  /**
+   * @override
    * @param {?SDK.TracingModel.Event} event
    */
   highlightEvent(event) {
diff --git a/third_party/WebKit/Source/devtools/front_end/timeline/TimelineLoader.js b/third_party/WebKit/Source/devtools/front_end/timeline/TimelineLoader.js
index e6180daa..8ad5a4698 100644
--- a/third_party/WebKit/Source/devtools/front_end/timeline/TimelineLoader.js
+++ b/third_party/WebKit/Source/devtools/front_end/timeline/TimelineLoader.js
@@ -9,7 +9,7 @@
 Timeline.TimelineLoader = class {
   /**
    * @param {!SDK.TracingModel} model
-   * @param {!Timeline.TimelineLifecycleDelegate} delegate
+   * @param {!Timeline.LoaderClient} delegate
    */
   constructor(model, delegate) {
     this._model = model;
@@ -46,7 +46,7 @@
   /**
    * @param {!SDK.TracingModel} model
    * @param {string} url
-   * @param {!Timeline.TimelineLifecycleDelegate} delegate
+   * @param {!Timeline.LoaderClient} delegate
    * @return {!Timeline.TimelineLoader}
    */
   static loadFromURL(model, url, delegate) {
diff --git a/third_party/WebKit/Source/devtools/front_end/timeline/TimelinePanel.js b/third_party/WebKit/Source/devtools/front_end/timeline/TimelinePanel.js
index 32353c98..7fe3956 100644
--- a/third_party/WebKit/Source/devtools/front_end/timeline/TimelinePanel.js
+++ b/third_party/WebKit/Source/devtools/front_end/timeline/TimelinePanel.js
@@ -67,10 +67,11 @@
         new TimelineModel.TimelineFrameModel(event => Timeline.TimelineUIUtils.eventStyle(event).category.name);
     this._filmStripModel = new Components.FilmStripModel(this._tracingModel);
     this._irModel = new TimelineModel.TimelineIRModel();
-
+    /** @type {!Array<!{title: string, model: !SDK.TracingModel}>} */
+    this._extensionTracingModels = [];
     this._cpuThrottlingManager = new Timeline.CPUThrottlingManager();
 
-    /** @type {!Array.<!Timeline.TimelineModeView>} */
+    /** @type {!Array<!Timeline.TimelineModeView>} */
     this._currentViews = [];
 
     this._captureNetworkSetting = Common.settings.createSetting('timelineCaptureNetwork', false);
@@ -118,6 +119,9 @@
     this._onModeChanged();
     this._recreateToolbarItems();
 
+    Extensions.extensionServer.addEventListener(
+        Extensions.ExtensionServer.Events.TraceProviderAdded, this._recreateToolbarItems, this);
+
     this._captureNetworkSetting.addChangeListener(this._onNetworkChanged, this);
     this._captureMemorySetting.addChangeListener(this._onModeChanged, this);
     this._captureFilmStripSetting.addChangeListener(this._onModeChanged, this);
@@ -131,6 +135,10 @@
     this._selectedSearchResult;
     /** @type {!Array<!SDK.TracingModel.Event>}|undefined */
     this._searchResults;
+    /** @type {?symbol} */
+    this._sessionGeneration = null;
+    /** @type {number} */
+    this._recordingStartTime = 0;
   }
 
   /**
@@ -383,6 +391,15 @@
           Common.UIString('CSS coverage'), this._markUnusedCSS, Common.UIString('Mark unused CSS in souces.')));
     }
 
+    const traceProviders = Extensions.extensionServer.traceProviders();
+    if (traceProviders.length) {
+      this._panelToolbar.appendSeparator();
+      for (let provider of traceProviders) {
+        const setting = Timeline.TimelinePanel._settingForTraceProvider(provider);
+        const checkbox = this._createSettingCheckbox(provider.shortDisplayName(), setting, provider.longDisplayName());
+        this._panelToolbar.appendToolbarItem(checkbox);
+      }
+    }
     this._panelToolbar.appendSeparator();
     this._panelToolbar.appendToolbarItem(UI.Toolbar.createActionButtonForId('components.collect-garbage'));
 
@@ -395,6 +412,20 @@
   }
 
   /**
+   * @param {!Extensions.ExtensionTraceProvider} traceProvider
+   * @return {!Common.Setting<boolean>}
+   */
+  static _settingForTraceProvider(traceProvider) {
+    var setting = traceProvider[Timeline.TimelinePanel._traceProviderSettingSymbol];
+    if (!setting) {
+      var providerId = traceProvider.persistentIdentifier();
+      setting = Common.settings.createSetting(providerId, false);
+      traceProvider[Timeline.TimelinePanel._traceProviderSettingSymbol] = setting;
+    }
+    return setting;
+  }
+
+  /**
    * @return {!UI.ToolbarComboBox}
    */
   _createNetworkConditionsSelect() {
@@ -530,8 +561,8 @@
 
     // Set up the main view.
     this._removeAllModeViews();
-    this._flameChart =
-        new Timeline.TimelineFlameChartView(this, this._model, this._frameModel, this._irModel, this._filters);
+    this._flameChart = new Timeline.TimelineFlameChartView(
+        this, this._model, this._frameModel, this._irModel, this._extensionTracingModels, this._filters);
     this._flameChart.enableNetworkPane(this._captureNetworkSetting.get());
     this._addModeView(this._flameChart);
 
@@ -583,12 +614,22 @@
     if (Runtime.experiments.isEnabled('timelineRuleUsageRecording') && this._markUnusedCSS.get())
       SDK.CSSModel.fromTarget(mainTarget).startRuleUsageTracking();
 
+    this._sessionGeneration = Symbol('timelineSessionGeneration');
     this._autoRecordGeneration = userInitiated ? null : Symbol('Generation');
+    var enabledTraceProviders = Extensions.extensionServer.traceProviders().filter(
+        provider => Timeline.TimelinePanel._settingForTraceProvider(provider).get());
+
+    var captureOptions = {
+      captureCauses: true,
+      enableJSSampling: this._captureJSProfileSetting.get(),
+      captureMemory: this._captureMemorySetting.get(),
+      capturePictures: this._captureLayersAndPicturesSetting.get(),
+      captureFilmStrip: this._captureFilmStripSetting.get()
+    };
+
     this._controller = new Timeline.TimelineController(mainTarget, this, this._tracingModel);
-    this._controller.startRecording(
-        true, this._captureJSProfileSetting.get(), this._captureMemorySetting.get(),
-        this._captureLayersAndPicturesSetting.get(),
-        this._captureFilmStripSetting && this._captureFilmStripSetting.get());
+    this._controller.startRecording(captureOptions, enabledTraceProviders);
+    this._recordingStartTime = Date.now();
 
     for (var i = 0; i < this._overviewControls.length; ++i)
       this._overviewControls[i].timelineStarted();
@@ -634,6 +675,8 @@
   _clear() {
     this._showRecordingHelpMessage();
     this._detailsSplitWidget.hideSidebar();
+    this._sessionGeneration = null;
+    this._recordingStartTime = 0;
     this._reset();
   }
 
@@ -644,6 +687,9 @@
     Components.LineLevelProfile.instance().reset();
     this._tracingModel.reset();
     this._model.reset();
+    for (let extensionEntry of this._extensionTracingModels)
+      extensionEntry.model.reset();
+    this._extensionTracingModels.splice(0);
 
     this.requestWindowTimes(0, Infinity);
     delete this._selection;
@@ -676,6 +722,29 @@
     this._statusPane.updateProgressBar(Common.UIString('Buffer usage'), usage * 100);
   }
 
+  /**
+   * @override
+   * @param {string} title
+   * @param {!SDK.TracingModel} tracingModel
+   * @param {number} timeOffset
+   */
+  addExtensionEvents(title, tracingModel, timeOffset) {
+    this._extensionTracingModels.push({title: title, model: tracingModel, timeOffset: timeOffset});
+    if (this._state !== Timeline.TimelinePanel.State.Idle)
+      return;
+    tracingModel.adjustTime(this._model.minimumRecordTime() + (timeOffset / 1000) - this._recordingStartTime);
+    for (let view of this._currentViews)
+      view.extensionDataAdded();
+  }
+
+  /**
+   * @override
+   * @return {?symbol}
+   */
+  sessionGeneration() {
+    return this._sessionGeneration;
+  }
+
   _showRecordingHelpMessage() {
     if (Runtime.experiments.isEnabled('timelineLandingPage')) {
       this._showLandingPage();
@@ -797,6 +866,10 @@
     if (this._statusPane)
       this._statusPane.hide();
     delete this._statusPane;
+
+    for (let entry of this._extensionTracingModels)
+      entry.model.adjustTime(this._model.minimumRecordTime() + (entry.timeOffset / 1000) - this._recordingStartTime);
+
     this._flameChart.resizeToPreferredHeights();
     this._overviewPane.reset();
     this._overviewPane.setBounds(this._model.minimumRecordTime(), this._model.maximumRecordTime());
@@ -1077,7 +1150,7 @@
    */
   _showSnapshotInPaintProfiler(snapshot) {
     var paintProfilerView = this._paintProfilerView();
-    var hasProfileData = paintProfilerView.setSnapshot(snapshot);
+    paintProfilerView.setSnapshot(snapshot);
     if (!this._detailsView.hasTab(Timeline.TimelinePanel.DetailsTab.PaintProfiler)) {
       this._detailsView.appendTab(
           Timeline.TimelinePanel.DetailsTab.PaintProfiler, Common.UIString('Paint Profiler'), paintProfilerView,
@@ -1299,16 +1372,9 @@
 /**
  * @interface
  */
-Timeline.TimelineLifecycleDelegate = function() {};
+Timeline.LoaderClient = function() {};
 
-Timeline.TimelineLifecycleDelegate.prototype = {
-  recordingStarted() {},
-
-  /**
-   * @param {number} usage
-   */
-  recordingProgress(usage) {},
-
+Timeline.LoaderClient.prototype = {
   loadingStarted() {},
 
   /**
@@ -1323,6 +1389,31 @@
 };
 
 /**
+ * @interface
+ * @extends {Timeline.LoaderClient}
+ */
+Timeline.TimelineLifecycleDelegate = function() {};
+
+Timeline.TimelineLifecycleDelegate.prototype = {
+  recordingStarted() {},
+
+  /**
+   * @param {number} usage
+   */
+  recordingProgress(usage) {},
+
+  /**
+   * @param {string} title
+   * @param {!SDK.TracingModel} tracingModel
+   * @param {number} timeOffset
+   */
+  addExtensionEvents(title, tracingModel, timeOffset) {},
+
+  /** @return {?symbol} */
+  sessionGeneration() {}
+};
+
+/**
  * @unrestricted
  */
 Timeline.TimelineDetailsView = class extends UI.TabbedPane {
@@ -1533,6 +1624,8 @@
 
   refreshRecords() {},
 
+  extensionDataAdded() {},
+
   /**
    * @param {?SDK.TracingModel.Event} event
    * @param {string=} regex
@@ -1922,6 +2015,8 @@
   }
 };
 
+Timeline.TimelinePanel._traceProviderSettingSymbol = Symbol('traceProviderSetting');
+
 /** @enum {symbol} */
 Timeline.TimelineFilters.Events = {
   FilterChanged: Symbol('FilterChanged')
diff --git a/third_party/WebKit/Source/devtools/front_end/timeline/TimelineUIUtils.js b/third_party/WebKit/Source/devtools/front_end/timeline/TimelineUIUtils.js
index 8fb0910..c1135885 100644
--- a/third_party/WebKit/Source/devtools/front_end/timeline/TimelineUIUtils.js
+++ b/third_party/WebKit/Source/devtools/front_end/timeline/TimelineUIUtils.js
@@ -993,8 +993,6 @@
    * @param {!Object} aggregatedStats
    */
   static _collectAggregatedStatsForRecord(record, startTime, endTime, aggregatedStats) {
-    var records = [];
-
     if (!record.endTime() || record.endTime() < startTime || record.startTime() > endTime)
       return;
 
@@ -1023,7 +1021,6 @@
     var contentHelper = new Timeline.TimelineDetailsContentHelper(target, linkifier);
 
     var duration = request.endTime - (request.startTime || -Infinity);
-    var items = [];
     if (request.url)
       contentHelper.appendElementRow(Common.UIString('URL'), Components.Linkifier.linkifyURL(request.url));
     if (isFinite(duration))
@@ -1511,7 +1508,6 @@
    * @return {!Element}
    */
   static generateDetailsContentForFrame(frameModel, frame, filmStripFrame) {
-    var pieChart = Timeline.TimelineUIUtils.generatePieChart(frame.timeByCategory);
     var contentHelper = new Timeline.TimelineDetailsContentHelper(null, null);
     contentHelper.addSection(Common.UIString('Frame'));
 
diff --git a/third_party/WebKit/Source/devtools/front_end/timeline/module.json b/third_party/WebKit/Source/devtools/front_end/timeline/module.json
index 205c09a..698db2cc 100644
--- a/third_party/WebKit/Source/devtools/front_end/timeline/module.json
+++ b/third_party/WebKit/Source/devtools/front_end/timeline/module.json
@@ -123,6 +123,7 @@
     ],
     "scripts": [
         "CountersGraph.js",
+        "ExtensionTracingSession.js",
         "MemoryCountersGraph.js",
         "TimelineController.js",
         "TimelineLoader.js",
diff --git a/third_party/WebKit/Source/devtools/front_end/timeline_model/TimelineFrameModel.js b/third_party/WebKit/Source/devtools/front_end/timeline_model/TimelineFrameModel.js
index ca15c78..2c3bc72 100644
--- a/third_party/WebKit/Source/devtools/front_end/timeline_model/TimelineFrameModel.js
+++ b/third_party/WebKit/Source/devtools/front_end/timeline_model/TimelineFrameModel.js
@@ -323,8 +323,6 @@
    */
   _addMainThreadTraceEvent(event) {
     var eventNames = TimelineModel.TimelineModel.RecordType;
-    var timestamp = event.startTime;
-    var selfTime = event.selfTime || 0;
 
     if (SDK.TracingModel.isTopLevelEvent(event)) {
       this._currentTaskTimeByCategory = {};
diff --git a/third_party/WebKit/Source/devtools/front_end/timeline_model/TimelineIRModel.js b/third_party/WebKit/Source/devtools/front_end/timeline_model/TimelineIRModel.js
index 3a70fcd..f505bf7 100644
--- a/third_party/WebKit/Source/devtools/front_end/timeline_model/TimelineIRModel.js
+++ b/third_party/WebKit/Source/devtools/front_end/timeline_model/TimelineIRModel.js
@@ -22,9 +22,6 @@
    * @param {?Array<!SDK.TracingModel.AsyncEvent>} animations
    */
   populate(inputLatencies, animations) {
-    var eventTypes = TimelineModel.TimelineIRModel.InputEvents;
-    var phases = TimelineModel.TimelineIRModel.Phases;
-
     this.reset();
     if (!inputLatencies)
       return;
diff --git a/third_party/WebKit/Source/devtools/front_end/ui/InplaceEditor.js b/third_party/WebKit/Source/devtools/front_end/ui/InplaceEditor.js
index 843d0f1f..836998c 100644
--- a/third_party/WebKit/Source/devtools/front_end/ui/InplaceEditor.js
+++ b/third_party/WebKit/Source/devtools/front_end/ui/InplaceEditor.js
@@ -100,13 +100,6 @@
     var moveDirection = '';
     var self = this;
 
-    /**
-     * @param {!Event} e
-     */
-    function consumeCopy(e) {
-      e.consume();
-    }
-
     this.setUpEditor(editingContext);
 
     editingContext.oldText = isMultiline ? config.initialValue : this.editorContent(editingContext);
diff --git a/third_party/WebKit/Source/devtools/front_end/ui/ListWidget.js b/third_party/WebKit/Source/devtools/front_end/ui/ListWidget.js
index 237c3e77..7878a136 100644
--- a/third_party/WebKit/Source/devtools/front_end/ui/ListWidget.js
+++ b/third_party/WebKit/Source/devtools/front_end/ui/ListWidget.js
@@ -114,7 +114,7 @@
    */
   _createControls(item, element) {
     var controls = createElementWithClass('div', 'controls-container fill');
-    var gradient = controls.createChild('div', 'controls-gradient');
+    controls.createChild('div', 'controls-gradient');
     var buttons = controls.createChild('div', 'controls-buttons');
 
     var editButton = buttons.createChild('div', 'edit-button');
diff --git a/third_party/WebKit/Source/devtools/front_end/ui/Widget.js b/third_party/WebKit/Source/devtools/front_end/ui/Widget.js
index 825ca18..a7525f6 100644
--- a/third_party/WebKit/Source/devtools/front_end/ui/Widget.js
+++ b/third_party/WebKit/Source/devtools/front_end/ui/Widget.js
@@ -384,7 +384,6 @@
       if (this._parentWidget._defaultFocusedChild === this)
         this._parentWidget._defaultFocusedChild = null;
       this._parentWidget.childWasDetached(this);
-      var parent = this._parentWidget;
       this._parentWidget = null;
       this._processWasDetachedFromHierarchy();
     } else {
diff --git a/third_party/WebKit/Source/devtools/front_end/ui/inspectorCommon.css b/third_party/WebKit/Source/devtools/front_end/ui/inspectorCommon.css
index cedc661..f0fb819 100644
--- a/third_party/WebKit/Source/devtools/front_end/ui/inspectorCommon.css
+++ b/third_party/WebKit/Source/devtools/front_end/ui/inspectorCommon.css
@@ -327,6 +327,10 @@
     flex-shrink: 0;
 }
 
+.icon-mask {
+    background-color: rgb(110, 110, 110);
+}
+
 .spritesheet-smallicons {
     background-image: -webkit-image-set(url(Images/smallIcons.png) 1x, url(Images/smallIcons_2x.png) 2x);
     background-size: 190px 30px;
diff --git a/third_party/WebKit/Source/devtools/front_end/ui_lazy/DataGrid.js b/third_party/WebKit/Source/devtools/front_end/ui_lazy/DataGrid.js
index e6031df..49d49e9a 100644
--- a/third_party/WebKit/Source/devtools/front_end/ui_lazy/DataGrid.js
+++ b/third_party/WebKit/Source/devtools/front_end/ui_lazy/DataGrid.js
@@ -602,7 +602,6 @@
       // when the two columns that get resized get a percent value for
       // their widths, all the other columns already have percent values
       // for their widths.
-      var headerTableColumns = this._headerTableColumnGroup.children;
 
       // Use container size to avoid changes of table width caused by change of column widths.
       var tableWidth = this.element.offsetWidth - this._cornerWidth;
diff --git a/third_party/WebKit/Source/devtools/front_end/workspace/UISourceCode.js b/third_party/WebKit/Source/devtools/front_end/workspace/UISourceCode.js
index 5a4b6735..12d57bd 100644
--- a/third_party/WebKit/Source/devtools/front_end/workspace/UISourceCode.js
+++ b/third_party/WebKit/Source/devtools/front_end/workspace/UISourceCode.js
@@ -173,7 +173,6 @@
    * @param {!Common.ResourceType=} contentType
    */
   _updateName(name, url, contentType) {
-    var oldURL = this.url();
     this._url = this._url.substring(0, this._url.length - this._name.length) + name;
     this._name = name;
     if (url)
diff --git a/third_party/WebKit/Source/platform/RuntimeEnabledFeatures.in b/third_party/WebKit/Source/platform/RuntimeEnabledFeatures.in
index d82f8b0..cc14099 100644
--- a/third_party/WebKit/Source/platform/RuntimeEnabledFeatures.in
+++ b/third_party/WebKit/Source/platform/RuntimeEnabledFeatures.in
@@ -95,6 +95,7 @@
 DOMConvenienceAPI status=stable
 DurableStorage status=stable
 ExpensiveBackgroundTimerThrottling status=experimental
+FasterLocationReload status=experimental
 FontCacheScaling status=test
 ForceDisplayList2dCanvas
 // See crbug.com/585250.
diff --git a/third_party/WebKit/Source/web/WebLocalFrameImpl.cpp b/third_party/WebKit/Source/web/WebLocalFrameImpl.cpp
index af4eb79..f230328 100644
--- a/third_party/WebKit/Source/web/WebLocalFrameImpl.cpp
+++ b/third_party/WebKit/Source/web/WebLocalFrameImpl.cpp
@@ -949,12 +949,7 @@
   // needs to be audited.  See http://crbug.com/590369 for more details.
   frame()->document()->updateStyleAndLayoutIgnorePendingStylesheets();
 
-  bool selectReplacement =
-      frame()->editor().behavior().shouldSelectReplacement();
-  bool smartReplace = true;
-  frame()->editor().replaceSelectionWithText(
-      text, selectReplacement, smartReplace,
-      InputEvent::InputType::InsertReplacementText);
+  frame()->editor().replaceSelection(text);
 }
 
 void WebLocalFrameImpl::setMarkedText(const WebString& text,
diff --git a/third_party/WebKit/Source/web/tests/RootScrollerTest.cpp b/third_party/WebKit/Source/web/tests/RootScrollerTest.cpp
index 0d80dda0..1038ec61 100644
--- a/third_party/WebKit/Source/web/tests/RootScrollerTest.cpp
+++ b/third_party/WebKit/Source/web/tests/RootScrollerTest.cpp
@@ -524,8 +524,9 @@
   // No root scroller set, the documentElement should be the effective root
   // and the main FrameView's scroll layer should be the layer to use.
   {
-    EXPECT_EQ(mainController.rootScrollerLayer(),
-              mainFrameView()->layerForScrolling());
+    EXPECT_EQ(
+        mainController.rootScrollerLayer(),
+        mainFrameView()->layoutViewportScrollableArea()->layerForScrolling());
     EXPECT_TRUE(mainController.isViewportScrollCallback(
         mainFrame()->document()->documentElement()->getApplyScroll()));
   }
@@ -536,8 +537,9 @@
     iframe->contentDocument()->setRootScroller(container, nonThrow);
     mainFrameView()->updateAllLifecyclePhases();
 
-    EXPECT_EQ(mainController.rootScrollerLayer(),
-              mainFrameView()->layerForScrolling());
+    EXPECT_EQ(
+        mainController.rootScrollerLayer(),
+        mainFrameView()->layoutViewportScrollableArea()->layerForScrolling());
     EXPECT_TRUE(mainController.isViewportScrollCallback(
         mainFrame()->document()->documentElement()->getApplyScroll()));
   }
@@ -568,7 +570,10 @@
     iframe->contentDocument()->setRootScroller(nullptr, nonThrow);
     mainFrameView()->updateAllLifecyclePhases();
     EXPECT_EQ(mainController.rootScrollerLayer(),
-              iframe->contentDocument()->view()->layerForScrolling());
+              iframe->contentDocument()
+                  ->view()
+                  ->layoutViewportScrollableArea()
+                  ->layerForScrolling());
     EXPECT_FALSE(
         mainController.isViewportScrollCallback(container->getApplyScroll()));
     EXPECT_FALSE(mainController.isViewportScrollCallback(
@@ -582,8 +587,9 @@
   {
     mainFrame()->document()->setRootScroller(nullptr, nonThrow);
     mainFrameView()->updateAllLifecyclePhases();
-    EXPECT_EQ(mainController.rootScrollerLayer(),
-              mainFrameView()->layerForScrolling());
+    EXPECT_EQ(
+        mainController.rootScrollerLayer(),
+        mainFrameView()->layoutViewportScrollableArea()->layerForScrolling());
     EXPECT_TRUE(mainController.isViewportScrollCallback(
         mainFrame()->document()->documentElement()->getApplyScroll()));
     EXPECT_FALSE(
@@ -729,6 +735,12 @@
   m_helper.reset();
 }
 
+GraphicsLayer* scrollingLayer(LayoutView& layoutView) {
+  if (RuntimeEnabledFeatures::rootLayerScrollingEnabled())
+    return layoutView.layer()->compositedLayerMapping()->scrollingLayer();
+  return layoutView.compositor()->rootContentLayer();
+}
+
 // Tests that clipping layers belonging to any compositors in the ancestor chain
 // of the global root scroller have their masking bit removed.
 TEST_F(RootScrollerTest, RemoveClippingOnCompositorLayers) {
@@ -745,10 +757,10 @@
   TopDocumentRootScrollerController& globalController =
       frameHost().globalRootScrollerController();
 
-  PaintLayerCompositor* mainCompositor =
-      mainFrameView()->layoutViewItem().compositor();
-  PaintLayerCompositor* childCompositor =
-      iframe->contentDocument()->view()->layoutViewItem().compositor();
+  LayoutView* mainLayoutView = mainFrameView()->layoutView();
+  LayoutView* childLayoutView = iframe->contentDocument()->layoutView();
+  PaintLayerCompositor* mainCompositor = mainLayoutView->compositor();
+  PaintLayerCompositor* childCompositor = childLayoutView->compositor();
 
   NonThrowableExceptionState nonThrow;
 
@@ -757,14 +769,14 @@
   // container layers should also clip.
   {
     EXPECT_TRUE(
-        mainCompositor->rootContentLayer()->platformLayer()->masksToBounds());
+        scrollingLayer(*mainLayoutView)->platformLayer()->masksToBounds());
     EXPECT_FALSE(
         mainCompositor->rootGraphicsLayer()->platformLayer()->masksToBounds());
     EXPECT_FALSE(
         mainCompositor->containerLayer()->platformLayer()->masksToBounds());
 
     EXPECT_TRUE(
-        childCompositor->rootContentLayer()->platformLayer()->masksToBounds());
+        scrollingLayer(*childLayoutView)->platformLayer()->masksToBounds());
     EXPECT_TRUE(
         childCompositor->rootGraphicsLayer()->platformLayer()->masksToBounds());
     EXPECT_TRUE(
@@ -783,14 +795,14 @@
     ASSERT_EQ(container, &childController.effectiveRootScroller());
 
     EXPECT_FALSE(
-        mainCompositor->rootContentLayer()->platformLayer()->masksToBounds());
+        scrollingLayer(*mainLayoutView)->platformLayer()->masksToBounds());
     EXPECT_FALSE(
         mainCompositor->rootGraphicsLayer()->platformLayer()->masksToBounds());
     EXPECT_FALSE(
         mainCompositor->containerLayer()->platformLayer()->masksToBounds());
 
     EXPECT_FALSE(
-        childCompositor->rootContentLayer()->platformLayer()->masksToBounds());
+        scrollingLayer(*childLayoutView)->platformLayer()->masksToBounds());
     EXPECT_FALSE(
         childCompositor->rootGraphicsLayer()->platformLayer()->masksToBounds());
     EXPECT_FALSE(
@@ -811,14 +823,14 @@
               globalController.globalRootScroller());
 
     EXPECT_FALSE(
-        mainCompositor->rootContentLayer()->platformLayer()->masksToBounds());
+        scrollingLayer(*mainLayoutView)->platformLayer()->masksToBounds());
     EXPECT_FALSE(
         mainCompositor->rootGraphicsLayer()->platformLayer()->masksToBounds());
     EXPECT_FALSE(
         mainCompositor->containerLayer()->platformLayer()->masksToBounds());
 
     EXPECT_TRUE(
-        childCompositor->rootContentLayer()->platformLayer()->masksToBounds());
+        scrollingLayer(*childLayoutView)->platformLayer()->masksToBounds());
     EXPECT_FALSE(
         childCompositor->rootGraphicsLayer()->platformLayer()->masksToBounds());
     EXPECT_FALSE(
@@ -842,14 +854,14 @@
               globalController.globalRootScroller());
 
     EXPECT_TRUE(
-        mainCompositor->rootContentLayer()->platformLayer()->masksToBounds());
+        scrollingLayer(*mainLayoutView)->platformLayer()->masksToBounds());
     EXPECT_FALSE(
         mainCompositor->rootGraphicsLayer()->platformLayer()->masksToBounds());
     EXPECT_FALSE(
         mainCompositor->containerLayer()->platformLayer()->masksToBounds());
 
     EXPECT_TRUE(
-        childCompositor->rootContentLayer()->platformLayer()->masksToBounds());
+        scrollingLayer(*childLayoutView)->platformLayer()->masksToBounds());
     EXPECT_TRUE(
         childCompositor->rootGraphicsLayer()->platformLayer()->masksToBounds());
     EXPECT_TRUE(
@@ -871,14 +883,14 @@
               globalController.globalRootScroller());
 
     EXPECT_FALSE(
-        mainCompositor->rootContentLayer()->platformLayer()->masksToBounds());
+        scrollingLayer(*mainLayoutView)->platformLayer()->masksToBounds());
     EXPECT_FALSE(
         mainCompositor->rootGraphicsLayer()->platformLayer()->masksToBounds());
     EXPECT_FALSE(
         mainCompositor->containerLayer()->platformLayer()->masksToBounds());
 
     EXPECT_TRUE(
-        childCompositor->rootContentLayer()->platformLayer()->masksToBounds());
+        scrollingLayer(*childLayoutView)->platformLayer()->masksToBounds());
     EXPECT_FALSE(
         childCompositor->rootGraphicsLayer()->platformLayer()->masksToBounds());
     EXPECT_FALSE(
@@ -896,14 +908,14 @@
     ASSERT_EQ(container, &childController.effectiveRootScroller());
 
     EXPECT_TRUE(
-        mainCompositor->rootContentLayer()->platformLayer()->masksToBounds());
+        scrollingLayer(*mainLayoutView)->platformLayer()->masksToBounds());
     EXPECT_FALSE(
         mainCompositor->rootGraphicsLayer()->platformLayer()->masksToBounds());
     EXPECT_FALSE(
         mainCompositor->containerLayer()->platformLayer()->masksToBounds());
 
     EXPECT_FALSE(
-        childCompositor->rootContentLayer()->platformLayer()->masksToBounds());
+        scrollingLayer(*childLayoutView)->platformLayer()->masksToBounds());
     EXPECT_FALSE(
         childCompositor->rootGraphicsLayer()->platformLayer()->masksToBounds());
     EXPECT_FALSE(
diff --git a/third_party/WebKit/Source/web/tests/WebFrameTest.cpp b/third_party/WebKit/Source/web/tests/WebFrameTest.cpp
index b28dd7a3..1a5df9e 100644
--- a/third_party/WebKit/Source/web/tests/WebFrameTest.cpp
+++ b/third_party/WebKit/Source/web/tests/WebFrameTest.cpp
@@ -4130,7 +4130,7 @@
 
   // Reload the page and end up at the same url. State should be propagated.
   webViewHelper.webView()->mainFrame()->reloadWithOverrideURL(
-      toKURL(m_baseURL + firstURL), WebFrameLoadType::Reload);
+      toKURL(m_baseURL + firstURL), WebFrameLoadType::ReloadMainResource);
   FrameTestHelpers::pumpPendingRequestsForFrameToLoad(
       webViewHelper.webView()->mainFrame());
   EXPECT_EQ(previousOffset.width,
@@ -4141,7 +4141,7 @@
 
   // Reload the page using the cache. State should not be propagated.
   webViewHelper.webView()->mainFrame()->reloadWithOverrideURL(
-      toKURL(m_baseURL + secondURL), WebFrameLoadType::Reload);
+      toKURL(m_baseURL + secondURL), WebFrameLoadType::ReloadMainResource);
   FrameTestHelpers::pumpPendingRequestsForFrameToLoad(
       webViewHelper.webView()->mainFrame());
   EXPECT_EQ(0, webViewHelper.webView()->mainFrame()->getScrollOffset().width);
diff --git a/third_party/WebKit/Source/web/tests/data/root-scroller-child.html b/third_party/WebKit/Source/web/tests/data/root-scroller-child.html
index 0223fdd..2c150d6 100644
--- a/third_party/WebKit/Source/web/tests/data/root-scroller-child.html
+++ b/third_party/WebKit/Source/web/tests/data/root-scroller-child.html
@@ -33,5 +33,6 @@
     <div id="container">
       <div id="spacer"></div>
     </div>
+    <div id="spacer"></div>
   </body>
 </html>
diff --git a/third_party/WebKit/Source/web/tests/data/root-scroller-iframe.html b/third_party/WebKit/Source/web/tests/data/root-scroller-iframe.html
index b6a6db1..4bb6e9b 100644
--- a/third_party/WebKit/Source/web/tests/data/root-scroller-iframe.html
+++ b/third_party/WebKit/Source/web/tests/data/root-scroller-iframe.html
@@ -21,10 +21,16 @@
         height: 100%;
         border-style: none;
       }
+
+      #spacer {
+        width: 800px;
+        height: 800px;
+      }
     </style>
   </head>
 
   <body>
     <iframe src="root-scroller-child.html" id="iframe"></iframe>
+    <div id="spacer"></div>
   </body>
 </html>
diff --git a/third_party/WebKit/public/web/WebFrame.h b/third_party/WebKit/public/web/WebFrame.h
index 2dae4ef..581e3c8 100644
--- a/third_party/WebKit/public/web/WebFrame.h
+++ b/third_party/WebKit/public/web/WebFrame.h
@@ -320,13 +320,12 @@
   // Reload the current document.
   // Note: reload() and reloadWithOverrideURL() will be deprecated.
   // Do not use these APIs any more, but use loadRequest() instead.
-  virtual void reload(WebFrameLoadType = WebFrameLoadType::Reload) = 0;
+  virtual void reload(WebFrameLoadType) = 0;
 
   // This is used for situations where we want to reload a different URL because
   // of a redirect.
-  virtual void reloadWithOverrideURL(
-      const WebURL& overrideUrl,
-      WebFrameLoadType = WebFrameLoadType::Reload) = 0;
+  virtual void reloadWithOverrideURL(const WebURL& overrideUrl,
+                                     WebFrameLoadType) = 0;
 
   // Load the given URL.
   virtual void loadRequest(const WebURLRequest&) = 0;
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml
index 168e7f1d..e856f24b 100644
--- a/tools/metrics/histograms/histograms.xml
+++ b/tools/metrics/histograms/histograms.xml
@@ -25912,6 +25912,13 @@
   </summary>
 </histogram>
 
+<histogram base="true" name="Media.WebMediaPlayerImpl.Memory" units="KB">
+  <owner>servolk@chromium.org</owner>
+  <summary>
+    Amount of memory used by the WebMediaPlayerImpl and its components.
+  </summary>
+</histogram>
+
 <histogram name="Media.WindowsCoreAudioInput" enum="BooleanSuccess">
   <owner>henrika@chromium.org</owner>
   <summary>
@@ -51249,6 +51256,16 @@
   <summary>Number of unlimited origins using temporary storage.</summary>
 </histogram>
 
+<histogram name="Quota.OSAccomodationDelta" units="MB">
+  <owner>michaeln@chromium.org</owner>
+  <summary>
+    If our hardcoded OS accomodation is too large for the volume size, we define
+    the value as a fraction of the total volume size instead. The
+    OSAccomodationDelta is the difference between the hardcoded and computed
+    values.
+  </summary>
+</histogram>
+
 <histogram name="Quota.QuotaForOrigin" units="MB">
   <owner>michaeln@chromium.org</owner>
   <summary>
@@ -51284,7 +51301,18 @@
   <summary>Time spent to an eviction round.</summary>
 </histogram>
 
+<histogram name="Quota.TimeToGetSettings" units="ms">
+  <owner>michaeln@chromium.org</owner>
+  <summary>
+    Time spent querying the embedder for the settings values. Logged at
+    irregular intervals as the values are refreshed.
+  </summary>
+</histogram>
+
 <histogram name="Quota.TimeToInitializeGlobalQuota" units="ms">
+  <obsolete>
+    Removed November 2016
+  </obsolete>
   <owner>michaeln@chromium.org</owner>
   <summary>
     Time spent initializing the global quota. Logged when the storage
@@ -53642,6 +53670,33 @@
   </summary>
 </histogram>
 
+<histogram name="SafeBrowsing.ReferrerAttributionResult"
+    enum="SafeBrowsingAttributionResultTypes">
+  <owner>jialiul@chromium.org</owner>
+  <summary>
+    The result of referrer attribution, including different types of success or
+    failure. This is incremented each time a safe browsing ping or download ping
+    is generated.
+  </summary>
+</histogram>
+
+<histogram name="SafeBrowsing.ReferrerHasInvalidTabID" enum="BooleanInvalid">
+  <owner>jialiul@chromium.org</owner>
+  <summary>
+    Number of times referrer attribution encounters an invalid tab ID. This is
+    incremented a safe browsing ping or download ping is generated and an
+    invalid tab ID is encountered during attribution.
+  </summary>
+</histogram>
+
+<histogram name="SafeBrowsing.ReferrerURLChainSize">
+  <owner>jialiul@chromium.org</owner>
+  <summary>
+    The length of referrer URL chain we get from referrer attribution. This is
+    incremented each time a safe browsing ping or download ping is generated.
+  </summary>
+</histogram>
+
 <histogram name="SafeBrowsing.UnverifiedDownloads.Allowed"
     enum="SBClientDownloadExtensions">
   <owner>asanka@chromium.org</owner>
@@ -101021,6 +101076,14 @@
   <int value="1" label="Kill (He's dead, Jim!)"/>
 </enum>
 
+<enum name="SafeBrowsingAttributionResultTypes" type="int">
+  <int value="1" label="SUCCESS"/>
+  <int value="2" label="SUCCESS_LANDING_PAGE"/>
+  <int value="3" label="SUCCESS_LANDING_REFERRER"/>
+  <int value="4" label="INVALID_URL"/>
+  <int value="5" label="NAVIGATION_EVENT_NOT_FOUND"/>
+</enum>
+
 <enum name="SafeBrowsingParseV4HashResult" type="int">
   <int value="0" label="PARSE_FROM_STRING_ERROR"/>
   <int value="1" label="UNEXPECTED_THREAT_ENTRY_TYPE_ERROR"/>
@@ -110584,6 +110647,14 @@
   <affected-histogram name="Media.WatchTime"/>
 </histogram_suffixes>
 
+<histogram_suffixes name="MediaWMPIMemoryUsage" separator=".">
+  <suffix name="Audio"/>
+  <suffix name="DataSource"/>
+  <suffix name="Demuxer"/>
+  <suffix name="Video"/>
+  <affected-histogram name="Media.WebMediaPlayerImpl.Memory"/>
+</histogram_suffixes>
+
 <histogram_suffixes name="MemoryStateTransition" separator=".">
   <suffix name="NormalToThrottled"/>
   <suffix name="NormalToSuspended"/>
@@ -113636,6 +113707,19 @@
   <affected-histogram name="Renderer4.ImageDecodeTaskDurationUs"/>
 </histogram_suffixes>
 
+<histogram_suffixes name="ReferrerAttribution" separator=".">
+  <suffix name="DownloadAttribution" label="Download referrer attribution."/>
+  <suffix name="PhishingInterstitialAttribution"
+      label="Phishing interstitial referrer attribution."/>
+  <suffix name="MalwareInterstitialAttribution"
+      label="Malware interstitial referrer attribution."/>
+  <suffix name="UwsInterstitialAttribution"
+      label="UwS interstitial referrer attribution."/>
+  <affected-histogram name="SafeBrowsing.ReferrerAttributionResult"/>
+  <affected-histogram name="SafeBrowsing.ReferrerHasInvalidTabID"/>
+  <affected-histogram name="SafeBrowsing.ReferrerURLChainSize"/>
+</histogram_suffixes>
+
 <histogram_suffixes name="RemoteProcessWarmStartFast" separator="">
   <suffix name="" label="Normal start."/>
   <suffix name="Fast"
diff --git a/ui/aura/mus/window_tree_host_mus.cc b/ui/aura/mus/window_tree_host_mus.cc
index 895ef4b..c5c314b02 100644
--- a/ui/aura/mus/window_tree_host_mus.cc
+++ b/ui/aura/mus/window_tree_host_mus.cc
@@ -108,9 +108,11 @@
   SetBoundsInPixels(bounds);
 }
 
-void WindowTreeHostMus::SetClientArea(const gfx::Insets& insets) {
+void WindowTreeHostMus::SetClientArea(
+    const gfx::Insets& insets,
+    const std::vector<gfx::Rect>& additional_client_area) {
   delegate_->OnWindowTreeHostClientAreaWillChange(this, insets,
-                                                  std::vector<gfx::Rect>());
+                                                  additional_client_area);
 }
 
 void WindowTreeHostMus::SetHitTestMask(const base::Optional<gfx::Rect>& rect) {
diff --git a/ui/aura/mus/window_tree_host_mus.h b/ui/aura/mus/window_tree_host_mus.h
index 4cf6f1b..0d923b4 100644
--- a/ui/aura/mus/window_tree_host_mus.h
+++ b/ui/aura/mus/window_tree_host_mus.h
@@ -61,7 +61,8 @@
   InputMethodMus* input_method() { return input_method_.get(); }
 
   // Sets the client area on the underlying mus window.
-  void SetClientArea(const gfx::Insets& insets);
+  void SetClientArea(const gfx::Insets& insets,
+                     const std::vector<gfx::Rect>& additional_client_area);
 
   // Sets the hit test mask on the underlying mus window. Pass base::nullopt to
   // clear.
diff --git a/ui/aura/mus/window_tree_host_mus_unittest.cc b/ui/aura/mus/window_tree_host_mus_unittest.cc
index fd2e93b..ac644ac 100644
--- a/ui/aura/mus/window_tree_host_mus_unittest.cc
+++ b/ui/aura/mus/window_tree_host_mus_unittest.cc
@@ -16,7 +16,7 @@
       base::MakeUnique<WindowTreeHostMus>(window_tree_client_impl());
 
   gfx::Insets new_insets(10, 11, 12, 13);
-  window_tree_host_mus->SetClientArea(new_insets);
+  window_tree_host_mus->SetClientArea(new_insets, std::vector<gfx::Rect>());
   EXPECT_EQ(new_insets, window_tree()->last_client_area());
 }
 
diff --git a/ui/file_manager/file_manager/foreground/elements/files_quick_view.css b/ui/file_manager/file_manager/foreground/elements/files_quick_view.css
index 0b31eea..242da315 100644
--- a/ui/file_manager/file_manager/foreground/elements/files_quick_view.css
+++ b/ui/file_manager/file_manager/foreground/elements/files_quick_view.css
@@ -50,7 +50,9 @@
 
 #contentPanel {
   background-color: transparent;
+  display: flex;
   height: 100%;
+  justify-content: center;
   position: relative;
 }
 
@@ -76,25 +78,26 @@
   color: white;
   display: flex;
   flex-direction: column;
-  height: 70%;
   justify-content: center;
-  margin: auto;
   outline: none;
+  padding: 24px 15%;
   position: relative;
   text-align: center;
-  top: 15%;
-  width: 70%;
+  width: 100%;
+}
+
+#innerContentPanel[type="audio"],
+#innerContentPanel[type="image"],
+#innerContentPanel[type="video"] {
+  align-self: center;
+  height: 70%;
+  padding-bottom: initial;
+  padding-top: initial;
 }
 
 .content {
-  bottom: 0;
-  left: 0;
-  margin: auto;
-  max-height: 100%;
-  max-width: 100%;
-  position: absolute;
-  right: 0;
-  top: 0;
+  height: 100%;
+  width: 100%;
 }
 
 #toolbar {
@@ -188,27 +191,28 @@
   height: 88px;
 }
 
+[generic-thumbnail=".folder"] {
+  background-image: -webkit-image-set(
+      url(../images/files/ui/quick_view/filetype_folder.png) 1x,
+      url(../images/files/ui/quick_view/2x/filetype_folder.png) 2x);
+  height: 72px;
+}
+
 [generic-thumbnail='audio'] {
-  background: -webkit-image-set(
+  background-image: -webkit-image-set(
       url(../images/files/ui/quick_view/filetype_audio.png) 1x,
-      url(../images/files/ui/quick_view/2x/filetype_audio.png) 2x)
-    center
-    no-repeat;
+      url(../images/files/ui/quick_view/2x/filetype_audio.png) 2x);
 }
 
 [generic-thumbnail='image'] {
-  background: -webkit-image-set(
+  background-image: -webkit-image-set(
       url(../images/files/ui/quick_view/filetype_image.png) 1x,
-      url(../images/files/ui/quick_view/2x/filetype_image.png) 2x)
-    center
-    no-repeat;
+      url(../images/files/ui/quick_view/2x/filetype_image.png) 2x);
 }
 
 [generic-thumbnail='video'] {
-  background: -webkit-image-set(
+  background-image: -webkit-image-set(
       url(../images/files/ui/quick_view/filetype_video.png) 1x,
-      url(../images/files/ui/quick_view/2x/filetype_video.png) 2x)
-    center
-    no-repeat;
+      url(../images/files/ui/quick_view/2x/filetype_video.png) 2x);
   height: 72px;
 }
diff --git a/ui/file_manager/file_manager/foreground/elements/files_quick_view.html b/ui/file_manager/file_manager/foreground/elements/files_quick_view.html
index bc34644..a70e66c 100644
--- a/ui/file_manager/file_manager/foreground/elements/files_quick_view.html
+++ b/ui/file_manager/file_manager/foreground/elements/files_quick_view.html
@@ -21,7 +21,7 @@
       <paper-toolbar id="toolbar">
         <div>[[filePath]]</div>
         <div id="buttons">
-           <paper-button id="open-button" on-tap="onOpenInNewButtonTap" hidden$="[[isUnsupported_(type)]]" i18n-values="aria-label:QUICK_VIEW_OPEN_IN_NEW_BUTTON_LABEL" tabindex="0" has-tooltip>
+           <paper-button id="open-button" on-tap="onOpenInNewButtonTap" hidden$="[[isUnsupported_(type, browsable)]]" i18n-values="aria-label:QUICK_VIEW_OPEN_IN_NEW_BUTTON_LABEL" tabindex="0" has-tooltip>
              <iron-icon icon="files:open-in-new"></iron-icon>
            </paper-button>
            <files-icon-button toggles id="metadata-button" on-tap="onMetadataButtonTap_" active="{{metadataBoxActive}}" i18n-values="aria-label:QUICK_VIEW_TOGGLE_METADATA_BOX_BUTTON_LABEL" tabindex="0" has-tooltip>
@@ -30,11 +30,14 @@
       </paper-toolbar>
       <div id="mainPanel">
         <div id="contentPanel" metadata-box-active$="[[metadataBoxActive]]" on-tap="onContentPanelTap_">
-          <div id="innerContentPanel" tabindex="0">
+          <div id="innerContentPanel" type$="[[type]]" tabindex="0">
+            <!-- PDF, Text -->
+            <template is="dom-if" if="[[browsable]]">
+              <webview class="content" src="[[contentUrl]]"></webview>
+            </template>
+            <!-- Image -->
             <template is="dom-if" if="[[isImage_(type)]]">
-              <div hidden="[[!contentUrl]]">
-                <files-safe-media type="image" class="content no-close-on-click" src="[[contentUrl]]"></files-safe-media>
-              </div>
+              <files-safe-media hidden="[[!contentUrl]]" type="image" class="content no-close-on-click" src="[[contentUrl]]"></files-safe-media>
               <template is="dom-if" if="[[!contentUrl]]">
                 <div generic-thumbnail="image"></div>
                 <div class="no-preivew">[[noPreviewText]]</div>
@@ -66,9 +69,8 @@
                 <div class="no-preivew">[[noPlaybackText]]</div>
               </template>
             </template>
-            <!-- TODO(oka): Support folder icon -->
-            <div hidden="[[!isUnsupported_(type)]]">
-              <div generic-thumbnail></div>
+            <div hidden="[[!isUnsupported_(type, browsable)]]">
+              <div generic-thumbnail$="[[type]]"></div>
               <div class="no-preview">[[noPreviewText]]</div>
             </div>
             </div> <!-- innerContentPanel -->
diff --git a/ui/file_manager/file_manager/foreground/elements/files_quick_view.js b/ui/file_manager/file_manager/foreground/elements/files_quick_view.js
index f887b5e3..c551cd52 100644
--- a/ui/file_manager/file_manager/foreground/elements/files_quick_view.js
+++ b/ui/file_manager/file_manager/foreground/elements/files_quick_view.js
@@ -15,6 +15,10 @@
     videoPoster: String,
     audioArtwork: String,
     autoplay: Boolean,
+    // True if this file is not image, audio nor video but supported on Chrome,
+    // i.e. preview-able by directly src-ing the file path to webview.
+    // Example: pdf, text.
+    browsable: Boolean,
 
     // metadata-box-active-changed event is fired on attribute change.
     metadataBoxActive: {
@@ -41,6 +45,7 @@
     this.videoPoster = '';
     this.audioArtwork = '';
     this.autoplay = false;
+    this.browsable = false;
   },
 
   // Opens the dialog.
@@ -154,8 +159,9 @@
    *
    * @private
    */
-  isUnsupported_: function(type) {
-    return !this.isImage_(type) && !this.isVideo_(type) && !this.isAudio_(type);
+  isUnsupported_: function(type, browsable) {
+    return !this.isImage_(type) && !this.isVideo_(type) &&
+        !this.isAudio_(type) && !browsable;
   },
 
 });
diff --git a/ui/file_manager/file_manager/foreground/js/quick_view_controller.js b/ui/file_manager/file_manager/foreground/js/quick_view_controller.js
index 2b04daa..c651a9e0 100644
--- a/ui/file_manager/file_manager/foreground/js/quick_view_controller.js
+++ b/ui/file_manager/file_manager/foreground/js/quick_view_controller.js
@@ -203,12 +203,13 @@
  */
 QuickViewController.prototype.onMetadataLoaded_ = function(entry, items) {
   return this.getQuickViewParameters_(entry, items).then(function(params) {
-    this.quickView_.contentUrl = params.contentUrl || '';
     this.quickView_.type = params.type || '';
     this.quickView_.filePath = params.filePath || '';
+    this.quickView_.contentUrl = params.contentUrl || '';
     this.quickView_.videoPoster = params.videoPoster || '';
     this.quickView_.audioArtwork = params.audioArtwork || '';
     this.quickView_.autoplay = params.autoplay || false;
+    this.quickView_.browsable = params.browsable || false;
   }.bind(this));
 };
 
@@ -219,7 +220,8 @@
  *   contentUrl: (string|undefined),
  *   videoPoster: (string|undefined),
  *   audioArtwork: (string|undefined),
- *   autoplay: (boolean|undefined)
+ *   autoplay: (boolean|undefined),
+ *   browsable: (boolean|undefined),
  * }}
  */
 var QuickViewParams;
@@ -311,7 +313,27 @@
           });
     }
   }
-  return Promise.resolve(params);
+  if (item.externalFileUrl || type === '.folder') {
+    return Promise.resolve(params);
+  }
+  return Promise
+      .all([
+        getFile(entry),
+        new Promise(function(resolve) {
+          chrome.fileManagerPrivate.getFileTasks([entry], resolve);
+        })
+      ])
+      .then(function(values) {
+        var file = values[0];
+        var tasks = values[1];
+        var browsable = tasks.some(function(task) {
+          return ['view-in-browser', 'view-pdf'].includes(
+              task.taskId.split('|')[2]);
+        });
+        params.browsable = browsable;
+        params.contentUrl = browsable && URL.createObjectURL(file);
+        return params;
+      });
 };
 
 /**
diff --git a/ui/views/mus/desktop_window_tree_host_mus.cc b/ui/views/mus/desktop_window_tree_host_mus.cc
index f502133..4b280c4dc 100644
--- a/ui/views/mus/desktop_window_tree_host_mus.cc
+++ b/ui/views/mus/desktop_window_tree_host_mus.cc
@@ -31,11 +31,6 @@
 
 namespace {
 
-bool ShouldSetClientArea(views::Widget::InitParams::Type type) {
-  using WIP = views::Widget::InitParams;
-  return type == WIP::TYPE_WINDOW || type == WIP::TYPE_PANEL;
-}
-
 // As the window manager renderers the non-client decorations this class does
 // very little but honor the client area insets from the window manager.
 class ClientSideNonClientFrameView : public NonClientFrameView {
@@ -201,7 +196,7 @@
 }
 
 void DesktopWindowTreeHostMus::SendClientAreaToServer() {
-  if (!ShouldSetClientArea(desktop_native_widget_aura_->widget_type()))
+  if (!ShouldSendClientAreaToServer())
     return;
 
   NonClientView* non_client_view =
@@ -210,10 +205,12 @@
     return;
 
   const gfx::Rect client_area_rect(non_client_view->client_view()->bounds());
-  SetClientArea(gfx::Insets(
-      client_area_rect.y(), client_area_rect.x(),
-      non_client_view->bounds().height() - client_area_rect.bottom(),
-      non_client_view->bounds().width() - client_area_rect.right()));
+  SetClientArea(
+      gfx::Insets(
+          client_area_rect.y(), client_area_rect.x(),
+          non_client_view->bounds().height() - client_area_rect.bottom(),
+          non_client_view->bounds().width() - client_area_rect.right()),
+      std::vector<gfx::Rect>());
 }
 
 void DesktopWindowTreeHostMus::SendHitTestMaskToServer() {
@@ -241,6 +238,15 @@
   SetBoundsInPixels(gfx::ConvertRectToPixel(GetScaleFactor(), bounds_in_dip));
 }
 
+bool DesktopWindowTreeHostMus::ShouldSendClientAreaToServer() const {
+  if (!auto_update_client_area_)
+    return false;
+
+  using WIP = views::Widget::InitParams;
+  const WIP::Type type = desktop_native_widget_aura_->widget_type();
+  return type == WIP::TYPE_WINDOW || type == WIP::TYPE_PANEL;
+}
+
 void DesktopWindowTreeHostMus::Init(aura::Window* content_window,
                                     const Widget::InitParams& params) {
   // Needed so we don't render over the non-client area the window manager
@@ -554,7 +560,7 @@
 }
 
 NonClientFrameView* DesktopWindowTreeHostMus::CreateNonClientFrameView() {
-  if (!ShouldSetClientArea(desktop_native_widget_aura_->widget_type()))
+  if (!ShouldSendClientAreaToServer())
     return nullptr;
 
   return new ClientSideNonClientFrameView(native_widget_delegate_->AsWidget());
diff --git a/ui/views/mus/desktop_window_tree_host_mus.h b/ui/views/mus/desktop_window_tree_host_mus.h
index 6603cf3..562eb0a 100644
--- a/ui/views/mus/desktop_window_tree_host_mus.h
+++ b/ui/views/mus/desktop_window_tree_host_mus.h
@@ -37,6 +37,11 @@
   // Called when the window was deleted on the server.
   void ServerDestroyedWindow() { CloseNow(); }
 
+  // Controls whether the client area is automatically updated as necessary.
+  void set_auto_update_client_area(bool value) {
+    auto_update_client_area_ = value;
+  }
+
  private:
   bool IsDocked() const;
 
@@ -48,6 +53,9 @@
 
   void SetBoundsInDIP(const gfx::Rect& bounds_in_dip);
 
+  // Returns true if the client area should be set on this.
+  bool ShouldSendClientAreaToServer() const;
+
   // DesktopWindowTreeHost:
   void Init(aura::Window* content_window,
             const Widget::InitParams& params) override;
@@ -142,6 +150,8 @@
 
   std::unique_ptr<wm::CursorManager> cursor_manager_;
 
+  bool auto_update_client_area_ = true;
+
   // Used so that Close() isn't immediate.
   base::WeakPtrFactory<DesktopWindowTreeHostMus> close_widget_factory_;
 
diff --git a/ui/views/style/platform_style.cc b/ui/views/style/platform_style.cc
index 1febd1f..c7cbab1 100644
--- a/ui/views/style/platform_style.cc
+++ b/ui/views/style/platform_style.cc
@@ -17,7 +17,9 @@
 #include "ui/views/controls/focusable_border.h"
 #include "ui/views/controls/scrollbar/scroll_bar_views.h"
 
-#if defined(OS_LINUX) && !defined(OS_CHROMEOS)
+#if defined(OS_CHROMEOS)
+#include "ui/views/controls/scrollbar/overlay_scroll_bar.h"
+#elif defined(OS_LINUX)
 #define DESKTOP_LINUX
 #endif
 
@@ -56,7 +58,11 @@
 
 // static
 std::unique_ptr<ScrollBar> PlatformStyle::CreateScrollBar(bool is_horizontal) {
+#if defined(OS_CHROMEOS)
+  return base::MakeUnique<OverlayScrollBar>(is_horizontal);
+#else
   return base::MakeUnique<ScrollBarViews>(is_horizontal);
+#endif
 }
 
 // static
diff --git a/ui/views_content_client/DEPS b/ui/views_content_client/DEPS
index 7f82ac5..5be0a24 100644
--- a/ui/views_content_client/DEPS
+++ b/ui/views_content_client/DEPS
@@ -1,6 +1,7 @@
 include_rules = [
   "+content/public",
   "+content/shell",
+  "+storage/browser/quota",
   "+ui/aura",
   "+ui/base",
   "+ui/display",
diff --git a/ui/views_content_client/views_content_browser_client.cc b/ui/views_content_client/views_content_browser_client.cc
index 29b6dec..f8c5a854 100644
--- a/ui/views_content_client/views_content_browser_client.cc
+++ b/ui/views_content_client/views_content_browser_client.cc
@@ -4,6 +4,10 @@
 
 #include "ui/views_content_client/views_content_browser_client.h"
 
+#include "content/public/browser/browser_context.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/storage_partition.h"
+#include "storage/browser/quota/quota_settings.h"
 #include "ui/views_content_client/views_content_client_main_parts.h"
 
 namespace ui {
@@ -24,4 +28,15 @@
   return views_content_main_parts_;
 }
 
+void ViewsContentBrowserClient::GetQuotaSettings(
+    content::BrowserContext* context,
+    content::StoragePartition* partition,
+    const storage::OptionalQuotaSettingsCallback& callback) {
+  content::BrowserThread::PostTaskAndReplyWithResult(
+      content::BrowserThread::FILE, FROM_HERE,
+      base::Bind(&storage::CalculateNominalDynamicSettings,
+                 partition->GetPath(), context->IsOffTheRecord()),
+      callback);
+}
+
 }  // namespace ui
diff --git a/ui/views_content_client/views_content_browser_client.h b/ui/views_content_client/views_content_browser_client.h
index 408fb8a..017ae63 100644
--- a/ui/views_content_client/views_content_browser_client.h
+++ b/ui/views_content_client/views_content_browser_client.h
@@ -22,6 +22,10 @@
   // content::ContentBrowserClient:
   content::BrowserMainParts* CreateBrowserMainParts(
       const content::MainFunctionParams& parameters) override;
+  void GetQuotaSettings(
+      content::BrowserContext* context,
+      content::StoragePartition* partition,
+      const storage::OptionalQuotaSettingsCallback& callback) override;
 
  private:
   ViewsContentClientMainParts* views_content_main_parts_;